1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 08:02:49 -05:00

Implemented Custom role and permissions (#1057)

* Implemented Custom role and permissions

* Converted permissions columns to a json blob

* Code review fixes for Permissions

* sql build fix

* Update Permissions.cs

* formatting

* Update IOrganizationService.cs

* reworked a conditional

* built out tests for relevant organization service methods

* removed unused usings

* fixed a broken test and a bad empty string init

* removed 'Attribute' from some attribute instances
This commit is contained in:
Addison Beck
2021-01-12 11:02:39 -05:00
committed by GitHub
parent 99b95b5330
commit 63fcdc1418
39 changed files with 1116 additions and 149 deletions

View File

@ -60,7 +60,7 @@ namespace Bit.Api.Controllers
{
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(new Guid(id));
if (cipher == null || !cipher.OrganizationId.HasValue ||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
!_currentContext.ManageAllCollections(cipher.OrganizationId.Value))
{
throw new NotFoundException();
}
@ -137,7 +137,7 @@ namespace Bit.Api.Controllers
public async Task<CipherMiniResponseModel> PostAdmin([FromBody]CipherCreateRequestModel model)
{
var cipher = model.Cipher.ToOrganizationCipher();
if (!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
if (!_currentContext.ManageAllCollections(cipher.OrganizationId.Value))
{
throw new NotFoundException();
}
@ -181,7 +181,7 @@ namespace Bit.Api.Controllers
var userId = _userService.GetProperUserId(User).Value;
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(new Guid(id));
if (cipher == null || !cipher.OrganizationId.HasValue ||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
!_currentContext.ManageAllCollections(cipher.OrganizationId.Value))
{
throw new NotFoundException();
}
@ -200,7 +200,7 @@ namespace Bit.Api.Controllers
{
var userId = _userService.GetProperUserId(User).Value;
var orgIdGuid = new Guid(organizationId);
if (!_currentContext.OrganizationAdmin(orgIdGuid))
if (!_currentContext.ManageAllCollections(orgIdGuid) && !_currentContext.AccessReports(orgIdGuid))
{
throw new NotFoundException();
}
@ -243,7 +243,7 @@ namespace Bit.Api.Controllers
}
var orgId = new Guid(organizationId);
if (!_currentContext.OrganizationAdmin(orgId))
if (!_currentContext.AccessImportExport(orgId))
{
throw new NotFoundException();
}
@ -308,7 +308,7 @@ namespace Bit.Api.Controllers
var userId = _userService.GetProperUserId(User).Value;
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
if (cipher == null || !cipher.OrganizationId.HasValue ||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
!_currentContext.ManageAllCollections(cipher.OrganizationId.Value))
{
throw new NotFoundException();
}
@ -338,7 +338,7 @@ namespace Bit.Api.Controllers
var userId = _userService.GetProperUserId(User).Value;
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
if (cipher == null || !cipher.OrganizationId.HasValue ||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
!_currentContext.ManageAllCollections(cipher.OrganizationId.Value))
{
throw new NotFoundException();
}
@ -371,7 +371,7 @@ namespace Bit.Api.Controllers
}
if (model == null || string.IsNullOrWhiteSpace(model.OrganizationId) ||
!_currentContext.OrganizationAdmin(new Guid(model.OrganizationId)))
!_currentContext.ManageAllCollections(new Guid(model.OrganizationId)))
{
throw new NotFoundException();
}
@ -398,7 +398,7 @@ namespace Bit.Api.Controllers
var userId = _userService.GetProperUserId(User).Value;
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
if (cipher == null || !cipher.OrganizationId.HasValue ||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
!_currentContext.ManageAllCollections(cipher.OrganizationId.Value))
{
throw new NotFoundException();
}
@ -427,7 +427,7 @@ namespace Bit.Api.Controllers
}
if (model == null || string.IsNullOrWhiteSpace(model.OrganizationId) ||
!_currentContext.OrganizationAdmin(new Guid(model.OrganizationId)))
!_currentContext.ManageAllCollections(new Guid(model.OrganizationId)))
{
throw new NotFoundException();
}
@ -456,7 +456,7 @@ namespace Bit.Api.Controllers
var userId = _userService.GetProperUserId(User).Value;
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(new Guid(id));
if (cipher == null || !cipher.OrganizationId.HasValue ||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
!_currentContext.ManageAllCollections(cipher.OrganizationId.Value))
{
throw new NotFoundException();
}
@ -550,7 +550,7 @@ namespace Bit.Api.Controllers
else
{
var orgId = new Guid(organizationId);
if (!_currentContext.OrganizationAdmin(orgId))
if (!_currentContext.ManageAllCollections(orgId))
{
throw new NotFoundException();
}
@ -593,7 +593,7 @@ namespace Bit.Api.Controllers
var userId = _userService.GetProperUserId(User).Value;
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(idGuid);
if (cipher == null || !cipher.OrganizationId.HasValue ||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
!_currentContext.ManageAllCollections(cipher.OrganizationId.Value))
{
throw new NotFoundException();
}
@ -651,7 +651,7 @@ namespace Bit.Api.Controllers
var userId = _userService.GetProperUserId(User).Value;
var cipher = await _cipherRepository.GetByIdAsync(idGuid);
if (cipher == null || !cipher.OrganizationId.HasValue ||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
!_currentContext.ManageAllCollections(cipher.OrganizationId.Value))
{
throw new NotFoundException();
}

View File

@ -45,13 +45,13 @@ namespace Bit.Api.Controllers
public async Task<CollectionGroupDetailsResponseModel> GetDetails(string orgId, string id)
{
var orgIdGuid = new Guid(orgId);
if (!_currentContext.OrganizationManager(orgIdGuid))
if (!ManageAnyCollections(orgIdGuid) && !_currentContext.ManageUsers(orgIdGuid))
{
throw new NotFoundException();
}
var idGuid = new Guid(id);
if (_currentContext.OrganizationAdmin(orgIdGuid))
if (_currentContext.ManageAllCollections(orgIdGuid))
{
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(idGuid);
if (collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgIdGuid)
@ -76,7 +76,7 @@ namespace Bit.Api.Controllers
public async Task<ListResponseModel<CollectionResponseModel>> Get(string orgId)
{
var orgIdGuid = new Guid(orgId);
if (!_currentContext.OrganizationAdmin(orgIdGuid))
if (!_currentContext.ManageAllCollections(orgIdGuid) && !_currentContext.ManageUsers(orgIdGuid))
{
throw new NotFoundException();
}
@ -108,14 +108,14 @@ namespace Bit.Api.Controllers
public async Task<CollectionResponseModel> Post(string orgId, [FromBody]CollectionRequestModel model)
{
var orgIdGuid = new Guid(orgId);
if (!_currentContext.OrganizationManager(orgIdGuid))
if (!ManageAnyCollections(orgIdGuid))
{
throw new NotFoundException();
}
var collection = model.ToCollection(orgIdGuid);
await _collectionService.SaveAsync(collection, model.Groups?.Select(g => g.ToSelectionReadOnly()),
!_currentContext.OrganizationAdmin(orgIdGuid) ? _currentContext.UserId : null);
!_currentContext.ManageAllCollections(orgIdGuid) ? _currentContext.UserId : null);
return new CollectionResponseModel(collection);
}
@ -154,7 +154,7 @@ namespace Bit.Api.Controllers
private async Task<Collection> GetCollectionAsync(Guid id, Guid orgId)
{
if (!_currentContext.OrganizationManager(orgId))
if (!ManageAnyCollections(orgId))
{
throw new NotFoundException();
}
@ -169,5 +169,10 @@ namespace Bit.Api.Controllers
return collection;
}
private bool ManageAnyCollections(Guid orgId)
{
return _currentContext.ManageAssignedCollections(orgId) || _currentContext.ManageAllCollections(orgId);
}
}
}

View File

@ -61,7 +61,7 @@ namespace Bit.Api.Controllers
var canView = false;
if (cipher.OrganizationId.HasValue)
{
canView = _currentContext.OrganizationAdmin(cipher.OrganizationId.Value);
canView = _currentContext.AccessEventLogs(cipher.OrganizationId.Value);
}
else if (cipher.UserId.HasValue)
{
@ -86,7 +86,7 @@ namespace Bit.Api.Controllers
[FromQuery]DateTime? start = null, [FromQuery]DateTime? end = null, [FromQuery]string continuationToken = null)
{
var orgId = new Guid(id);
if (!_currentContext.OrganizationAdmin(orgId))
if (!_currentContext.AccessEventLogs(orgId))
{
throw new NotFoundException();
}
@ -104,7 +104,7 @@ namespace Bit.Api.Controllers
{
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
if (organizationUser == null || !organizationUser.UserId.HasValue ||
!_currentContext.OrganizationAdmin(organizationUser.OrganizationId))
!_currentContext.AccessEventLogs(organizationUser.OrganizationId))
{
throw new NotFoundException();
}

View File

@ -34,7 +34,7 @@ namespace Bit.Api.Controllers
public async Task<GroupResponseModel> Get(string orgId, string id)
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
if (group == null || !_currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}
@ -46,7 +46,7 @@ namespace Bit.Api.Controllers
public async Task<GroupDetailsResponseModel> GetDetails(string orgId, string id)
{
var groupDetails = await _groupRepository.GetByIdWithCollectionsAsync(new Guid(id));
if (groupDetails?.Item1 == null || !_currentContext.OrganizationAdmin(groupDetails.Item1.OrganizationId))
if (groupDetails?.Item1 == null || !_currentContext.ManageGroups(groupDetails.Item1.OrganizationId))
{
throw new NotFoundException();
}
@ -58,7 +58,11 @@ namespace Bit.Api.Controllers
public async Task<ListResponseModel<GroupResponseModel>> Get(string orgId)
{
var orgIdGuid = new Guid(orgId);
if (!_currentContext.OrganizationManager(orgIdGuid))
var canAccess = _currentContext.ManageGroups(orgIdGuid) ||
_currentContext.ManageAssignedCollections(orgIdGuid) ||
_currentContext.ManageAllCollections(orgIdGuid);
if (!canAccess)
{
throw new NotFoundException();
}
@ -73,7 +77,7 @@ namespace Bit.Api.Controllers
{
var idGuid = new Guid(id);
var group = await _groupRepository.GetByIdAsync(idGuid);
if (group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
if (group == null || !_currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}
@ -86,7 +90,7 @@ namespace Bit.Api.Controllers
public async Task<GroupResponseModel> Post(string orgId, [FromBody]GroupRequestModel model)
{
var orgIdGuid = new Guid(orgId);
if (!_currentContext.OrganizationAdmin(orgIdGuid))
if (!_currentContext.ManageGroups(orgIdGuid))
{
throw new NotFoundException();
}
@ -101,7 +105,7 @@ namespace Bit.Api.Controllers
public async Task<GroupResponseModel> Put(string orgId, string id, [FromBody]GroupRequestModel model)
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
if (group == null || !_currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}
@ -114,7 +118,7 @@ namespace Bit.Api.Controllers
public async Task PutUsers(string orgId, string id, [FromBody]IEnumerable<Guid> model)
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
if (group == null || !_currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}
@ -126,7 +130,7 @@ namespace Bit.Api.Controllers
public async Task Delete(string orgId, string id)
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
if (group == null || !_currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}
@ -139,7 +143,7 @@ namespace Bit.Api.Controllers
public async Task Delete(string orgId, string id, string orgUserId)
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
if (group == null || !_currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}

View File

@ -9,6 +9,7 @@ using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core;
using System.Collections.Generic;
using Bit.Core.Models.Business;
namespace Bit.Api.Controllers
{
@ -46,7 +47,7 @@ namespace Bit.Api.Controllers
public async Task<OrganizationUserDetailsResponseModel> Get(string orgId, string id)
{
var organizationUser = await _organizationUserRepository.GetByIdWithCollectionsAsync(new Guid(id));
if (organizationUser == null || !_currentContext.OrganizationAdmin(organizationUser.Item1.OrganizationId))
if (organizationUser == null || !_currentContext.ManageUsers(organizationUser.Item1.OrganizationId))
{
throw new NotFoundException();
}
@ -58,7 +59,7 @@ namespace Bit.Api.Controllers
public async Task<ListResponseModel<OrganizationUserUserDetailsResponseModel>> Get(string orgId)
{
var orgGuidId = new Guid(orgId);
if (!_currentContext.OrganizationManager(orgGuidId))
if (!_currentContext.ManageAssignedCollections(orgGuidId) && !_currentContext.ManageGroups(orgGuidId))
{
throw new NotFoundException();
}
@ -74,7 +75,7 @@ namespace Bit.Api.Controllers
public async Task<IEnumerable<string>> GetGroups(string orgId, string id)
{
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
if (organizationUser == null || !_currentContext.OrganizationAdmin(organizationUser.OrganizationId))
if (organizationUser == null || !_currentContext.ManageGroups(organizationUser.OrganizationId))
{
throw new NotFoundException();
}
@ -88,21 +89,20 @@ namespace Bit.Api.Controllers
public async Task Invite(string orgId, [FromBody]OrganizationUserInviteRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!_currentContext.OrganizationAdmin(orgGuidId))
if (!_currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var result = await _organizationService.InviteUserAsync(orgGuidId, userId.Value, model.Emails, model.Type.Value,
model.AccessAll, null, model.Collections?.Select(c => c.ToSelectionReadOnly()));
var result = await _organizationService.InviteUserAsync(orgGuidId, userId.Value, null, new OrganizationUserInvite(model));
}
[HttpPost("{id}/reinvite")]
public async Task Reinvite(string orgId, string id)
{
var orgGuidId = new Guid(orgId);
if (!_currentContext.OrganizationAdmin(orgGuidId))
if (!_currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
@ -127,7 +127,7 @@ namespace Bit.Api.Controllers
public async Task Confirm(string orgId, string id, [FromBody]OrganizationUserConfirmRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!_currentContext.OrganizationAdmin(orgGuidId))
if (!_currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
@ -142,7 +142,7 @@ namespace Bit.Api.Controllers
public async Task Put(string orgId, string id, [FromBody]OrganizationUserUpdateRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!_currentContext.OrganizationAdmin(orgGuidId))
if (!_currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
@ -163,7 +163,7 @@ namespace Bit.Api.Controllers
public async Task PutGroups(string orgId, string id, [FromBody]OrganizationUserUpdateGroupsRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!_currentContext.OrganizationAdmin(orgGuidId))
if (!_currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
@ -174,7 +174,8 @@ namespace Bit.Api.Controllers
throw new NotFoundException();
}
await _organizationService.UpdateUserGroupsAsync(organizationUser, model.GroupIds.Select(g => new Guid(g)));
var loggedInUserId = _userService.GetProperUserId(User);
await _organizationService.UpdateUserGroupsAsync(organizationUser, model.GroupIds.Select(g => new Guid(g)), loggedInUserId);
}
[HttpDelete("{id}")]
@ -182,7 +183,7 @@ namespace Bit.Api.Controllers
public async Task Delete(string orgId, string id)
{
var orgGuidId = new Guid(orgId);
if (!_currentContext.OrganizationAdmin(orgGuidId))
if (!_currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}

View File

@ -52,7 +52,7 @@ namespace Bit.Api.Controllers
public async Task<PolicyResponseModel> Get(string orgId, int type)
{
var orgIdGuid = new Guid(orgId);
if (!_currentContext.OrganizationAdmin(orgIdGuid))
if (!_currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}
@ -69,7 +69,7 @@ namespace Bit.Api.Controllers
public async Task<ListResponseModel<PolicyResponseModel>> Get(string orgId)
{
var orgIdGuid = new Guid(orgId);
if (!_currentContext.OrganizationManager(orgIdGuid))
if (!_currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}
@ -108,7 +108,7 @@ namespace Bit.Api.Controllers
public async Task<PolicyResponseModel> Put(string orgId, int type, [FromBody]PolicyRequestModel model)
{
var orgIdGuid = new Guid(orgId);
if (!_currentContext.OrganizationAdmin(orgIdGuid))
if (!_currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}

View File

@ -167,7 +167,7 @@ namespace Bit.Api.Controllers
var user = await CheckAsync(model.MasterPasswordHash, false);
var orgIdGuid = new Guid(id);
if (!_currentContext.OrganizationAdmin(orgIdGuid))
if (!_currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}
@ -190,7 +190,7 @@ namespace Bit.Api.Controllers
var user = await CheckAsync(model.MasterPasswordHash, false);
var orgIdGuid = new Guid(id);
if (!_currentContext.OrganizationAdmin(orgIdGuid))
if (!_currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}
@ -331,7 +331,7 @@ namespace Bit.Api.Controllers
var user = await CheckAsync(model.MasterPasswordHash, false);
var orgIdGuid = new Guid(id);
if (!_currentContext.OrganizationAdmin(orgIdGuid))
if (!_currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}

View File

@ -5,6 +5,7 @@ using System.Net;
using System.Threading.Tasks;
using Bit.Core;
using Bit.Core.Models.Api.Public;
using Bit.Core.Models.Business;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
@ -116,8 +117,15 @@ namespace Bit.Api.Public.Controllers
public async Task<IActionResult> Post([FromBody]MemberCreateRequestModel model)
{
var associations = model.Collections?.Select(c => c.ToSelectionReadOnly());
var user = await _organizationService.InviteUserAsync(_currentContext.OrganizationId.Value, null,
model.Email, model.Type.Value, model.AccessAll.Value, model.ExternalId, associations);
var invite = new OrganizationUserInvite
{
Emails = new List<string> { model.Email },
Type = model.Type.Value,
AccessAll = model.AccessAll.Value,
Collections = associations
};
var userPromise = await _organizationService.InviteUserAsync(_currentContext.OrganizationId.Value, null, model.ExternalId, invite);
var user = userPromise.FirstOrDefault();
var response = new MemberResponseModel(user, associations);
return new JsonResult(response);
}
@ -178,7 +186,7 @@ namespace Bit.Api.Public.Controllers
{
return new NotFoundResult();
}
await _organizationService.UpdateUserGroupsAsync(existingUser, model.GroupIds);
await _organizationService.UpdateUserGroupsAsync(existingUser, model.GroupIds, null);
return new OkResult();
}