1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 15:42:48 -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:
Rui Tomé
2023-05-17 14:17:37 +01:00
committed by GitHub
parent 8262af3c53
commit bcf096971b
4 changed files with 235 additions and 19 deletions

View File

@ -1,6 +1,7 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using Bit.Core.Models;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Core.Entities;
@ -28,4 +29,10 @@ public class OrganizationUser : ITableObject<Guid>, IExternal
{
Id = CoreHelpers.GenerateComb();
}
public Permissions GetPermissions()
{
return string.IsNullOrWhiteSpace(Permissions) ? null
: CoreHelpers.LoadClassFromJsonData<Permissions>(Permissions);
}
}

View File

@ -986,10 +986,10 @@ public class OrganizationService : IOrganizationService
.Select(i => i.invite.Type.Value));
if (invitingUserId.HasValue && inviteTypes.Count > 0)
{
foreach (var type in inviteTypes)
foreach (var (invite, _) in invites)
{
await ValidateOrganizationUserUpdatePermissions(organizationId, type, null);
await ValidateOrganizationCustomPermissionsEnabledAsync(organizationId, type);
await ValidateOrganizationUserUpdatePermissions(organizationId, invite.Type.Value, null, invite.Permissions);
await ValidateOrganizationCustomPermissionsEnabledAsync(organizationId, invite.Type.Value);
}
}
@ -1536,7 +1536,7 @@ public class OrganizationService : IOrganizationService
if (savingUserId.HasValue)
{
await ValidateOrganizationUserUpdatePermissions(user.OrganizationId, user.Type, originalUser.Type);
await ValidateOrganizationUserUpdatePermissions(user.OrganizationId, user.Type, originalUser.Type, user.GetPermissions());
}
await ValidateOrganizationCustomPermissionsEnabledAsync(user.OrganizationId, user.Type);
@ -1709,7 +1709,7 @@ public class OrganizationService : IOrganizationService
{
if (loggedInUserId.HasValue)
{
await ValidateOrganizationUserUpdatePermissions(organizationUser.OrganizationId, organizationUser.Type, null);
await ValidateOrganizationUserUpdatePermissions(organizationUser.OrganizationId, organizationUser.Type, null, organizationUser.GetPermissions());
}
await _organizationUserRepository.UpdateGroupsAsync(organizationUser.Id, groupIds);
await _eventService.LogOrganizationUserEventAsync(organizationUser,
@ -2100,8 +2100,7 @@ public class OrganizationService : IOrganizationService
}
}
private async Task ValidateOrganizationUserUpdatePermissions(Guid organizationId, OrganizationUserType newType,
OrganizationUserType? oldType)
private async Task ValidateOrganizationUserUpdatePermissions(Guid organizationId, OrganizationUserType newType, OrganizationUserType? oldType, Permissions permissions)
{
if (await _currentContext.OrganizationOwner(organizationId))
{
@ -2118,11 +2117,6 @@ public class OrganizationService : IOrganizationService
return;
}
if (oldType == OrganizationUserType.Custom || newType == OrganizationUserType.Custom)
{
throw new BadRequestException("Only Owners and Admins can configure Custom accounts.");
}
if (!await _currentContext.ManageUsers(organizationId))
{
throw new BadRequestException("Your account does not have permission to manage users.");
@ -2132,6 +2126,11 @@ public class OrganizationService : IOrganizationService
{
throw new BadRequestException("Custom users can not manage Admins or Owners.");
}
if (newType == OrganizationUserType.Custom && !await ValidateCustomPermissionsGrant(organizationId, permissions))
{
throw new BadRequestException("Custom users can only grant the same custom permissions that they have.");
}
}
private async Task ValidateOrganizationCustomPermissionsEnabledAsync(Guid organizationId, OrganizationUserType newType)
@ -2153,6 +2152,86 @@ public class OrganizationService : IOrganizationService
}
}
private async Task<bool> ValidateCustomPermissionsGrant(Guid organizationId, Permissions permissions)
{
if (permissions == null || await _currentContext.OrganizationOwner(organizationId) || await _currentContext.OrganizationAdmin(organizationId))
{
return true;
}
if (permissions.ManageUsers && !await _currentContext.ManageUsers(organizationId))
{
return false;
}
if (permissions.AccessReports && !await _currentContext.AccessReports(organizationId))
{
return false;
}
if (permissions.ManageGroups && !await _currentContext.ManageGroups(organizationId))
{
return false;
}
if (permissions.ManagePolicies && !await _currentContext.ManagePolicies(organizationId))
{
return false;
}
if (permissions.ManageScim && !await _currentContext.ManageScim(organizationId))
{
return false;
}
if (permissions.ManageSso && !await _currentContext.ManageSso(organizationId))
{
return false;
}
if (permissions.AccessEventLogs && !await _currentContext.AccessEventLogs(organizationId))
{
return false;
}
if (permissions.AccessImportExport && !await _currentContext.AccessImportExport(organizationId))
{
return false;
}
if (permissions.CreateNewCollections && !await _currentContext.CreateNewCollections(organizationId))
{
return false;
}
if (permissions.DeleteAnyCollection && !await _currentContext.DeleteAnyCollection(organizationId))
{
return false;
}
if (permissions.DeleteAssignedCollections && !await _currentContext.DeleteAssignedCollections(organizationId))
{
return false;
}
if (permissions.EditAnyCollection && !await _currentContext.EditAnyCollection(organizationId))
{
return false;
}
if (permissions.EditAssignedCollections && !await _currentContext.EditAssignedCollections(organizationId))
{
return false;
}
if (permissions.ManageResetPassword && !await _currentContext.ManageResetPassword(organizationId))
{
return false;
}
return true;
}
private async Task ValidateDeleteOrganizationAsync(Organization organization)
{
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id);