mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 07:36:14 -05:00
[PM-1879] Allow custom users to grant the same custom permissions that they have (#2897)
* [PM-1879] Replaced JsonSerializer.Serialize with CoreHelpers.ClassToJsonData * [PM-1879] Changed OrganizationService.SaveUserAsync to check Custom permissions * [PM-1879] Added unit tests for saving Custom permissions using a Custom user * [PM-1879] Added method OrganizationUser.GetPermissions to deserialize the Permissions property * [PM-1879] Refactored ValidateCustomPermissionsGrant to return bool * [PM-1879] Added unit test SaveUser_WithCustomPermission_WhenUpgradingToAdmin_Throws
This commit is contained in:
@ -33,7 +33,6 @@ namespace Bit.Core.Test.Services;
|
||||
[SutProviderCustomize]
|
||||
public class OrganizationServiceTests
|
||||
{
|
||||
// [Fact]
|
||||
[Theory, PaidOrganizationCustomize, BitAutoData]
|
||||
public async Task OrgImportCreateNewUsers(SutProvider<OrganizationService> sutProvider, Guid userId,
|
||||
Organization org, List<OrganizationUserUserDetails> existingUsers, List<ImportedOrganizationUser> newUsers)
|
||||
@ -200,6 +199,7 @@ public class OrganizationServiceTests
|
||||
OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
invite.Emails = null;
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(true);
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
await Assert.ThrowsAsync<NotFoundException>(
|
||||
@ -281,7 +281,7 @@ public class OrganizationServiceTests
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, new (OrganizationUserInvite, string)[] { (invite, null) }));
|
||||
Assert.Contains("only owners and admins", exception.Message.ToLowerInvariant());
|
||||
Assert.Contains("your account does not have permission to manage users", exception.Message.ToLowerInvariant());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@ -463,6 +463,19 @@ public class OrganizationServiceTests
|
||||
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
||||
.Returns(new[] { owner });
|
||||
currentContext.ManageUsers(organization.Id).Returns(true);
|
||||
currentContext.AccessReports(organization.Id).Returns(true);
|
||||
currentContext.ManageGroups(organization.Id).Returns(true);
|
||||
currentContext.ManagePolicies(organization.Id).Returns(true);
|
||||
currentContext.ManageScim(organization.Id).Returns(true);
|
||||
currentContext.ManageSso(organization.Id).Returns(true);
|
||||
currentContext.AccessEventLogs(organization.Id).Returns(true);
|
||||
currentContext.AccessImportExport(organization.Id).Returns(true);
|
||||
currentContext.CreateNewCollections(organization.Id).Returns(true);
|
||||
currentContext.DeleteAnyCollection(organization.Id).Returns(true);
|
||||
currentContext.DeleteAssignedCollections(organization.Id).Returns(true);
|
||||
currentContext.EditAnyCollection(organization.Id).Returns(true);
|
||||
currentContext.EditAssignedCollections(organization.Id).Returns(true);
|
||||
currentContext.ManageResetPassword(organization.Id).Returns(true);
|
||||
|
||||
await sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, invites);
|
||||
|
||||
@ -535,6 +548,7 @@ public class OrganizationServiceTests
|
||||
OrganizationUser newUserData,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
Permissions permissions,
|
||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
@ -547,6 +561,10 @@ public class OrganizationServiceTests
|
||||
newUserData.Id = oldUserData.Id;
|
||||
newUserData.UserId = oldUserData.UserId;
|
||||
newUserData.OrganizationId = savingUser.OrganizationId = oldUserData.OrganizationId = organization.Id;
|
||||
newUserData.Permissions = JsonSerializer.Serialize(permissions, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
});
|
||||
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
||||
organizationUserRepository.GetManyByOrganizationAsync(savingUser.OrganizationId, OrganizationUserType.Owner)
|
||||
.Returns(new List<OrganizationUser> { savingUser });
|
||||
@ -576,6 +594,7 @@ public class OrganizationServiceTests
|
||||
newUserData.Id = oldUserData.Id;
|
||||
newUserData.UserId = oldUserData.UserId;
|
||||
newUserData.OrganizationId = savingUser.OrganizationId = oldUserData.OrganizationId = organization.Id;
|
||||
newUserData.Permissions = null;
|
||||
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
||||
organizationUserRepository.GetManyByOrganizationAsync(savingUser.OrganizationId, OrganizationUserType.Owner)
|
||||
.Returns(new List<OrganizationUser> { savingUser });
|
||||
@ -598,6 +617,7 @@ public class OrganizationServiceTests
|
||||
OrganizationUser newUserData,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
Permissions permissions,
|
||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
@ -613,6 +633,10 @@ public class OrganizationServiceTests
|
||||
newUserData.UserId = oldUserData.UserId;
|
||||
newUserData.OrganizationId = savingUser.OrganizationId = oldUserData.OrganizationId = organization.Id;
|
||||
newUserData.Type = newUserType;
|
||||
newUserData.Permissions = JsonSerializer.Serialize(permissions, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
});
|
||||
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
||||
organizationUserRepository.GetManyByOrganizationAsync(savingUser.OrganizationId, OrganizationUserType.Owner)
|
||||
.Returns(new List<OrganizationUser> { savingUser });
|
||||
@ -628,6 +652,7 @@ public class OrganizationServiceTests
|
||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser newUserData,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
Permissions permissions,
|
||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
@ -642,6 +667,10 @@ public class OrganizationServiceTests
|
||||
newUserData.Id = oldUserData.Id;
|
||||
newUserData.UserId = oldUserData.UserId;
|
||||
newUserData.OrganizationId = savingUser.OrganizationId = oldUserData.OrganizationId = organization.Id;
|
||||
newUserData.Permissions = JsonSerializer.Serialize(permissions, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
});
|
||||
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
||||
organizationUserRepository.GetManyByOrganizationAsync(savingUser.OrganizationId, OrganizationUserType.Owner)
|
||||
.Returns(new List<OrganizationUser> { savingUser });
|
||||
@ -650,6 +679,111 @@ public class OrganizationServiceTests
|
||||
await sutProvider.Sut.SaveUserAsync(newUserData, savingUser.UserId, collections, groups);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SaveUser_WithCustomPermission_WhenSavingUserHasCustomPermission_Passes(
|
||||
Organization organization,
|
||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser oldUserData,
|
||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser newUserData,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser savingUser,
|
||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationOwner,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
organization.UseCustomPermissions = true;
|
||||
|
||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
||||
|
||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
newUserData.Id = oldUserData.Id;
|
||||
newUserData.UserId = oldUserData.UserId;
|
||||
newUserData.OrganizationId = savingUser.OrganizationId = oldUserData.OrganizationId = organizationOwner.OrganizationId = organization.Id;
|
||||
newUserData.Permissions = JsonSerializer.Serialize(new Permissions { AccessReports = true }, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
});
|
||||
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
||||
organizationUserRepository.GetManyByOrganizationAsync(savingUser.OrganizationId, OrganizationUserType.Owner)
|
||||
.Returns(new List<OrganizationUser> { organizationOwner });
|
||||
currentContext.OrganizationCustom(savingUser.OrganizationId).Returns(true);
|
||||
currentContext.ManageUsers(savingUser.OrganizationId).Returns(true);
|
||||
currentContext.AccessReports(savingUser.OrganizationId).Returns(true);
|
||||
|
||||
await sutProvider.Sut.SaveUserAsync(newUserData, savingUser.UserId, collections, groups);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SaveUser_WithCustomPermission_WhenSavingUserDoesNotHaveCustomPermission_Throws(
|
||||
Organization organization,
|
||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser oldUserData,
|
||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser newUserData,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser savingUser,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
organization.UseCustomPermissions = true;
|
||||
|
||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
||||
|
||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
newUserData.Id = oldUserData.Id;
|
||||
newUserData.UserId = oldUserData.UserId;
|
||||
newUserData.OrganizationId = savingUser.OrganizationId = oldUserData.OrganizationId = organization.Id;
|
||||
newUserData.Permissions = JsonSerializer.Serialize(new Permissions { AccessReports = true }, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
});
|
||||
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
||||
currentContext.OrganizationCustom(savingUser.OrganizationId).Returns(true);
|
||||
currentContext.ManageUsers(savingUser.OrganizationId).Returns(true);
|
||||
currentContext.AccessReports(savingUser.OrganizationId).Returns(false);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.SaveUserAsync(newUserData, savingUser.UserId, collections, groups));
|
||||
Assert.Contains("custom users can only grant the same custom permissions that they have", exception.Message.ToLowerInvariant());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SaveUser_WithCustomPermission_WhenUpgradingToAdmin_Throws(
|
||||
Organization organization,
|
||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser oldUserData,
|
||||
[OrganizationUser(type: OrganizationUserType.Admin)] OrganizationUser newUserData,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
organization.UseCustomPermissions = true;
|
||||
|
||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
||||
|
||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
newUserData.Id = oldUserData.Id;
|
||||
newUserData.UserId = oldUserData.UserId;
|
||||
newUserData.OrganizationId = oldUserData.OrganizationId = organization.Id;
|
||||
newUserData.Permissions = JsonSerializer.Serialize(new Permissions { AccessReports = true }, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
});
|
||||
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
||||
currentContext.OrganizationCustom(oldUserData.OrganizationId).Returns(true);
|
||||
currentContext.ManageUsers(oldUserData.OrganizationId).Returns(true);
|
||||
currentContext.AccessReports(oldUserData.OrganizationId).Returns(false);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.SaveUserAsync(newUserData, oldUserData.UserId, collections, groups));
|
||||
Assert.Contains("custom users can not manage admins or owners", exception.Message.ToLowerInvariant());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task DeleteUser_InvalidUser(OrganizationUser organizationUser, OrganizationUser deletingUser,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
|
Reference in New Issue
Block a user