Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AC-1125] Enforce org setting for creating/deleting collections #3241

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4e9d9db
[AC-1117] Add manage permission (#3126)
differsthecat Aug 11, 2023
ccb35db
[AC-1374] Limit collection creation/deletion to Owner/Admin (#3145)
vincentsalucci Aug 14, 2023
554511f
feat: created collection auth handler/operations, added LimitCollecti…
vincentsalucci Aug 18, 2023
fb71c7a
feat: create vault service collection extensions and register with ba…
vincentsalucci Aug 18, 2023
e8ea48c
Merge branch 'feature/flexible-collections' into ac/ac-1125/enforce-c…
vincentsalucci Aug 24, 2023
bf35f7e
feat: deprecated CurrentContext.CreateNewCollections, refs AC-1125
vincentsalucci Aug 25, 2023
3a000d5
feat: deprecate DeleteAnyCollection for single resource usages, refs …
vincentsalucci Aug 28, 2023
714d33a
feat: move service registration to api, update references, refs AC-1125
vincentsalucci Aug 28, 2023
f63bedf
feat: add bulk delete authorization handler, refs AC-1125
vincentsalucci Aug 30, 2023
8ad6342
feat: always assign user and give manage access on create, refs AC-1125
vincentsalucci Aug 30, 2023
9c16507
Merge branch 'feature/flexible-collections' into ac/ac-1125/enforce-c…
vincentsalucci Aug 30, 2023
b92503b
Merge branch 'feature/flexible-collections' into ac/ac-1125/enforce-c…
vincentsalucci Aug 31, 2023
8a134b9
fix: updated CurrentContextOrganization type, refs AC-1125
vincentsalucci Aug 31, 2023
be21fd0
feat: combined existing collection authorization handlers/operations,…
vincentsalucci Aug 31, 2023
2fe8c7f
fix: OrganizationServiceTests -> CurrentContentOrganization typo, ref…
vincentsalucci Aug 31, 2023
8fb8e90
fix: format, refs AC-1125
vincentsalucci Aug 31, 2023
3b8b1ef
fix: update collection controller tests, refs AC-1125
vincentsalucci Aug 31, 2023
838c30a
Merge branch 'feature/flexible-collections' into ac/ac-1125/enforce-c…
vincentsalucci Sep 1, 2023
80601c8
fix: dotnet format, refs AC-1125
vincentsalucci Sep 1, 2023
77c743b
feat: removed extra BulkAuthorizationHandler, refs AC-1125
vincentsalucci Sep 1, 2023
20ebf79
fix: dotnet format, refs AC-1125
vincentsalucci Sep 1, 2023
2e1979b
Merge branch 'feature/flexible-collections' into ac/ac-1125/enforce-c…
vincentsalucci Sep 5, 2023
56cf003
fix: change string to guid for org id, update bulk delete request mod…
vincentsalucci Sep 7, 2023
a357ee7
fix: remove delete many collection check, refs AC-1125
vincentsalucci Sep 7, 2023
bf15afa
fix: clean up collection auth handler, refs AC-1125
vincentsalucci Sep 8, 2023
5daa6a4
fix: format fix for CollectionOperations, refs AC-1125
vincentsalucci Sep 8, 2023
594ec31
fix: removed unnecessary owner check, add org null check to custom pe…
vincentsalucci Sep 8, 2023
1fcaba5
fix: remove unused methods in CurrentContext, refs AC-1125
vincentsalucci Sep 8, 2023
7e23011
fix: removed obsolete test, fixed failling delete many test, refs AC-…
vincentsalucci Sep 8, 2023
e2272c1
fix: CollectionAuthorizationHandlerTests fixes, refs AC-1125
vincentsalucci Sep 8, 2023
c716e5d
fix: OrganizationServiceTests fix broken test by mocking GetOrganizat…
vincentsalucci Sep 8, 2023
3d9358a
fix: CollectionAuthorizationHandler - remove unused repository, refs …
vincentsalucci Sep 8, 2023
36301f6
feat: moved UserId null check to common method, refs AC-1125
vincentsalucci Sep 11, 2023
9bd3c66
fix: updated auth handler tests to remove dependency on requirement f…
vincentsalucci Sep 11, 2023
ec9b180
feat: updated conditionals/comments for create/delete methods within …
vincentsalucci Sep 12, 2023
85460e6
feat: added create/delete collection auth handler success methods, re…
vincentsalucci Sep 12, 2023
6166ae1
Merge branch 'feature/flexible-collections' into ac/ac-1125/enforce-c…
vincentsalucci Sep 12, 2023
bf26926
fix: new up permissions to prevent excessive null checks, refs AC-1125
vincentsalucci Sep 12, 2023
20b35f2
fix: remove old reference to CreateNewCollections, refs AC-1125
vincentsalucci Sep 13, 2023
b477d6d
Merge branch 'feature/flexible-collections' into ac/ac-1125/enforce-c…
vincentsalucci Sep 13, 2023
bed42d0
fix: typo within ViewAssignedCollections method, refs AC-1125
vincentsalucci Sep 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 25 additions & 57 deletions src/Api/Controllers/CollectionsController.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Bit.Api.Models.Request;
using Bit.Api.Models.Response;
using Bit.Api.Vault.AuthorizationHandlers.Collections;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
Expand All @@ -20,19 +21,22 @@ public class CollectionsController : Controller
private readonly IDeleteCollectionCommand _deleteCollectionCommand;
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
private readonly IAuthorizationService _authorizationService;

public CollectionsController(
ICollectionRepository collectionRepository,
ICollectionService collectionService,
IDeleteCollectionCommand deleteCollectionCommand,
IUserService userService,
ICurrentContext currentContext)
ICurrentContext currentContext,
IAuthorizationService authorizationService)
{
_collectionRepository = collectionRepository;
_collectionService = collectionService;
_deleteCollectionCommand = deleteCollectionCommand;
_userService = userService;
_currentContext = currentContext;
_authorizationService = authorizationService;
}

[HttpGet("{id}")]
Expand Down Expand Up @@ -62,6 +66,7 @@ public async Task<CollectionAccessDetailsResponseModel> GetDetails(Guid orgId, G
{
throw new NotFoundException();
}

return new CollectionAccessDetailsResponseModel(collection, access.Groups, access.Users);
}
else
Expand All @@ -72,20 +77,23 @@ public async Task<CollectionAccessDetailsResponseModel> GetDetails(Guid orgId, G
{
throw new NotFoundException();
}

return new CollectionAccessDetailsResponseModel(collection, access.Groups, access.Users);
}
}

[HttpGet("details")]
public async Task<ListResponseModel<CollectionAccessDetailsResponseModel>> GetManyWithDetails(Guid orgId)
{
if (!await ViewAtLeastOneCollectionAsync(orgId) && !await _currentContext.ManageUsers(orgId) && !await _currentContext.ManageGroups(orgId))
if (!await ViewAtLeastOneCollectionAsync(orgId) && !await _currentContext.ManageUsers(orgId) &&
!await _currentContext.ManageGroups(orgId))
{
throw new NotFoundException();
}

// We always need to know which collections the current user is assigned to
var assignedOrgCollections = await _collectionRepository.GetManyByUserIdWithAccessAsync(_currentContext.UserId.Value, orgId);
var assignedOrgCollections =
await _collectionRepository.GetManyByUserIdWithAccessAsync(_currentContext.UserId.Value, orgId);

if (await _currentContext.ViewAllCollections(orgId) || await _currentContext.ManageUsers(orgId))
{
Expand Down Expand Up @@ -141,19 +149,16 @@ public async Task<CollectionResponseModel> Post(Guid orgId, [FromBody] Collectio
{
var collection = model.ToCollection(orgId);

if (!await CanCreateCollection(orgId, collection.Id) &&
!await CanEditCollectionAsync(orgId, collection.Id))
var result = await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Create);
if (!result.Succeeded)
{
throw new NotFoundException();
}

var groups = model.Groups?.Select(g => g.ToSelectionReadOnly());
var users = model.Users?.Select(g => g.ToSelectionReadOnly());

var assignUserToCollection = !(await _currentContext.EditAnyCollection(orgId)) &&
await _currentContext.EditAssignedCollections(orgId);

await _collectionService.SaveAsync(collection, groups, users, assignUserToCollection ? _currentContext.UserId : null);
await _collectionService.SaveAsync(collection, groups, users, _currentContext.UserId);
eliykat marked this conversation as resolved.
Show resolved Hide resolved
return new CollectionResponseModel(collection);
}

Expand Down Expand Up @@ -189,35 +194,28 @@ public async Task PutUsers(Guid orgId, Guid id, [FromBody] IEnumerable<Selection
[HttpPost("{id}/delete")]
public async Task Delete(Guid orgId, Guid id)
{
if (!await CanDeleteCollectionAsync(orgId, id))
var collection = await GetCollectionAsync(id, orgId);
shane-melton marked this conversation as resolved.
Show resolved Hide resolved
var result = await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Delete);
if (!result.Succeeded)
{
throw new NotFoundException();
}

var collection = await GetCollectionAsync(id, orgId);
await _deleteCollectionCommand.DeleteAsync(collection);
}

[HttpDelete("")]
[HttpPost("delete")]
public async Task DeleteMany([FromBody] CollectionBulkDeleteRequestModel model)
{
var orgId = new Guid(model.OrganizationId);
var collectionIds = model.Ids.Select(i => new Guid(i));
if (!await _currentContext.DeleteAssignedCollections(orgId) && !await _currentContext.DeleteAnyCollection(orgId))
var collections = await _collectionRepository.GetManyByManyIdsAsync(model.Ids);
var result = await _authorizationService.AuthorizeAsync(User, collections, CollectionOperations.Delete);
if (!result.Succeeded)
{
throw new NotFoundException();
}

var userCollections = await _collectionService.GetOrganizationCollectionsAsync(orgId);
var filteredCollections = userCollections.Where(c => collectionIds.Contains(c.Id) && c.OrganizationId == orgId);

if (!filteredCollections.Any())
{
throw new BadRequestException("No collections found.");
}

await _deleteCollectionCommand.DeleteManyAsync(filteredCollections);
await _deleteCollectionCommand.DeleteManyAsync(collections);
}

[HttpDelete("{id}/user/{orgUserId}")]
Expand Down Expand Up @@ -248,17 +246,6 @@ private async Task<Collection> GetCollectionAsync(Guid id, Guid orgId)
return collection;
}


private async Task<bool> CanCreateCollection(Guid orgId, Guid collectionId)
{
if (collectionId != default)
{
return false;
}

return await _currentContext.CreateNewCollections(orgId);
}

private async Task<bool> CanEditCollectionAsync(Guid orgId, Guid collectionId)
{
if (collectionId == default)
Expand All @@ -273,28 +260,8 @@ private async Task<bool> CanEditCollectionAsync(Guid orgId, Guid collectionId)

if (await _currentContext.EditAssignedCollections(orgId))
{
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
return collectionDetails != null;
}

return false;
}

private async Task<bool> CanDeleteCollectionAsync(Guid orgId, Guid collectionId)
{
if (collectionId == default)
{
return false;
}

if (await _currentContext.DeleteAnyCollection(orgId))
{
return true;
}

if (await _currentContext.DeleteAssignedCollections(orgId))
{
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
var collectionDetails =
await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
return collectionDetails != null;
}

Expand All @@ -315,7 +282,8 @@ private async Task<bool> CanViewCollectionAsync(Guid orgId, Guid collectionId)

if (await _currentContext.ViewAssignedCollections(orgId))
{
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
var collectionDetails =
await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
return collectionDetails != null;
}

Expand Down
3 changes: 2 additions & 1 deletion src/Api/Models/Request/CollectionRequestModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public virtual Collection ToCollection(Collection existingCollection)
public class CollectionBulkDeleteRequestModel
{
[Required]
public IEnumerable<string> Ids { get; set; }
public IEnumerable<Guid> Ids { get; set; }
[Obsolete("OrganizationId is no longer required and will be removed in a future release")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Why don't we just remove this instead of making it obsolete? We don't use it anywhere in the code, so its not keeping anything backwards compatible as far as I can tell?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a task for it. Just cleanliness (imo). Trying to keep this PR within its original scope while also being able to group small, one-off PRs for server/client with the removal of the property.

public string OrganizationId { get; set; }
}

Expand Down
2 changes: 1 addition & 1 deletion src/Api/Utilities/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Bit.Api.Vault.AuthorizationHandlers;
using Bit.Api.Vault.AuthorizationHandlers.Collections;
using Bit.Core.IdentityServer;
using Bit.Core.Settings;
using Bit.Core.Utilities;
Expand Down

This file was deleted.

Loading
Loading