1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-03 09:02:48 -05:00

Restore old logic behind flags

This commit is contained in:
Thomas Rittson
2023-10-09 13:55:14 +10:00
parent 7aabe7ca59
commit 8ebac62dff
3 changed files with 121 additions and 12 deletions

View File

@ -1,12 +1,14 @@
using Bit.Api.Models.Request; using Bit.Api.Models.Request;
using Bit.Api.Models.Response; using Bit.Api.Models.Response;
using Bit.Api.Vault.AuthorizationHandlers.Collections; using Bit.Api.Vault.AuthorizationHandlers.Collections;
using Bit.Core;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.OrganizationFeatures.OrganizationCollections.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationCollections.Interfaces;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -23,6 +25,7 @@ public class CollectionsController : Controller
private readonly IAuthorizationService _authorizationService; private readonly IAuthorizationService _authorizationService;
private readonly ICurrentContext _currentContext; private readonly ICurrentContext _currentContext;
private readonly IBulkAddCollectionAccessCommand _bulkAddCollectionAccessCommand; private readonly IBulkAddCollectionAccessCommand _bulkAddCollectionAccessCommand;
private readonly IFeatureService _featureService;
public CollectionsController( public CollectionsController(
ICollectionRepository collectionRepository, ICollectionRepository collectionRepository,
@ -31,7 +34,8 @@ public class CollectionsController : Controller
IUserService userService, IUserService userService,
IAuthorizationService authorizationService, IAuthorizationService authorizationService,
ICurrentContext currentContext, ICurrentContext currentContext,
IBulkAddCollectionAccessCommand bulkAddCollectionAccessCommand) IBulkAddCollectionAccessCommand bulkAddCollectionAccessCommand,
IFeatureService featureService)
{ {
_collectionRepository = collectionRepository; _collectionRepository = collectionRepository;
_collectionService = collectionService; _collectionService = collectionService;
@ -40,6 +44,7 @@ public class CollectionsController : Controller
_authorizationService = authorizationService; _authorizationService = authorizationService;
_currentContext = currentContext; _currentContext = currentContext;
_bulkAddCollectionAccessCommand = bulkAddCollectionAccessCommand; _bulkAddCollectionAccessCommand = bulkAddCollectionAccessCommand;
_featureService = featureService;
} }
[HttpGet("{id}")] [HttpGet("{id}")]
@ -152,8 +157,10 @@ public class CollectionsController : Controller
{ {
var collection = model.ToCollection(orgId); var collection = model.ToCollection(orgId);
var result = await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Create); var authorized = FlexibleCollectionsIsEnabled()
if (!result.Succeeded) ? (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Create)).Succeeded
: await CanCreateCollection(orgId, collection.Id) && await CanEditCollectionAsync(orgId, collection.Id);
if (!authorized)
{ {
throw new NotFoundException(); throw new NotFoundException();
} }
@ -221,8 +228,11 @@ public class CollectionsController : Controller
public async Task Delete(Guid orgId, Guid id) public async Task Delete(Guid orgId, Guid id)
{ {
var collection = await GetCollectionAsync(id, orgId); var collection = await GetCollectionAsync(id, orgId);
var result = await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Delete);
if (!result.Succeeded) var authorized = FlexibleCollectionsIsEnabled()
? (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Delete)).Succeeded
: await CanDeleteCollectionAsync(orgId, id);
if (!authorized)
{ {
throw new NotFoundException(); throw new NotFoundException();
} }
@ -232,8 +242,11 @@ public class CollectionsController : Controller
[HttpDelete("")] [HttpDelete("")]
[HttpPost("delete")] [HttpPost("delete")]
public async Task DeleteMany([FromBody] CollectionBulkDeleteRequestModel model) public async Task DeleteMany(Guid orgId, [FromBody] CollectionBulkDeleteRequestModel model)
{ {
if (FlexibleCollectionsIsEnabled())
{
// New flexible collections logic
var collections = await _collectionRepository.GetManyByManyIdsAsync(model.Ids); var collections = await _collectionRepository.GetManyByManyIdsAsync(model.Ids);
var result = await _authorizationService.AuthorizeAsync(User, collections, CollectionOperations.Delete); var result = await _authorizationService.AuthorizeAsync(User, collections, CollectionOperations.Delete);
if (!result.Succeeded) if (!result.Succeeded)
@ -244,6 +257,24 @@ public class CollectionsController : Controller
await _deleteCollectionCommand.DeleteManyAsync(collections); await _deleteCollectionCommand.DeleteManyAsync(collections);
} }
// Old pre-flexible collections logic follows
if (!await _currentContext.DeleteAssignedCollections(orgId) && !await DeleteAnyCollection(orgId))
{
throw new NotFoundException();
}
var userCollections = await _collectionService.GetOrganizationCollectionsAsync(orgId);
var filteredCollections = userCollections
.Where(c => model.Ids.Contains(c.Id) && c.OrganizationId == orgId);
if (!filteredCollections.Any())
{
throw new BadRequestException("No collections found.");
}
await _deleteCollectionCommand.DeleteManyAsync(filteredCollections);
}
[HttpDelete("{id}/user/{orgUserId}")] [HttpDelete("{id}/user/{orgUserId}")]
[HttpPost("{id}/delete-user/{orgUserId}")] [HttpPost("{id}/delete-user/{orgUserId}")]
public async Task Delete(string orgId, string id, string orgUserId) public async Task Delete(string orgId, string id, string orgUserId)
@ -272,6 +303,33 @@ public class CollectionsController : Controller
return collection; return collection;
} }
private bool FlexibleCollectionsIsEnabled()
{
return _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext);
}
private void DeprecatedPermissionsGuard()
{
if (FlexibleCollectionsIsEnabled())
{
throw new FeatureUnavailableException("Flexible Collections is ON when it should be OFF.");
}
}
[Obsolete("Pre-Flexible Collections logic. Will be replaced by CollectionsAuthorizationHandler.")]
private async Task<bool> CanCreateCollection(Guid orgId, Guid collectionId)
{
DeprecatedPermissionsGuard();
if (collectionId != default)
{
return false;
}
return await _currentContext.OrganizationManager(orgId) || (_currentContext.Organizations?.Any(o => o.Id == orgId &&
(o.Permissions?.CreateNewCollections ?? false)) ?? false);
}
private async Task<bool> CanEditCollectionAsync(Guid orgId, Guid collectionId) private async Task<bool> CanEditCollectionAsync(Guid orgId, Guid collectionId)
{ {
if (collectionId == default) if (collectionId == default)
@ -294,6 +352,41 @@ public class CollectionsController : Controller
return false; return false;
} }
[Obsolete("Pre-Flexible Collections logic. Will be replaced by CollectionsAuthorizationHandler.")]
private async Task<bool> CanDeleteCollectionAsync(Guid orgId, Guid collectionId)
{
DeprecatedPermissionsGuard();
if (collectionId == default)
{
return false;
}
if (await DeleteAnyCollection(orgId))
{
return true;
}
if (await _currentContext.DeleteAssignedCollections(orgId))
{
var collectionDetails =
await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
return collectionDetails != null;
}
return false;
}
[Obsolete("Pre-Flexible Collections logic. Will be replaced by CollectionsAuthorizationHandler.")]
private async Task<bool> DeleteAnyCollection(Guid orgId)
{
DeprecatedPermissionsGuard();
return await _currentContext.OrganizationAdmin(orgId) ||
(_currentContext.Organizations?.Any(o => o.Id == orgId
&& (o.Permissions?.DeleteAnyCollection ?? false)) ?? false);
}
private async Task<bool> CanViewCollectionAsync(Guid orgId, Guid collectionId) private async Task<bool> CanViewCollectionAsync(Guid orgId, Guid collectionId)
{ {
if (collectionId == default) if (collectionId == default)

View File

@ -1,27 +1,42 @@
using Bit.Core.Context; using Bit.Core;
using Bit.Core.Context;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
namespace Bit.Api.Vault.AuthorizationHandlers.Collections; namespace Bit.Api.Vault.AuthorizationHandlers.Collections;
/// <summary>
/// Handles authorization logic for Collection objects, including access permissions for users and groups.
/// This uses new logic implemented in the Flexible Collections initiative.
/// </summary>
public class CollectionAuthorizationHandler : BulkAuthorizationHandler<CollectionOperationRequirement, Collection> public class CollectionAuthorizationHandler : BulkAuthorizationHandler<CollectionOperationRequirement, Collection>
{ {
private readonly ICurrentContext _currentContext; private readonly ICurrentContext _currentContext;
private readonly ICollectionRepository _collectionRepository; private readonly ICollectionRepository _collectionRepository;
private readonly IFeatureService _featureService;
public CollectionAuthorizationHandler(ICurrentContext currentContext, ICollectionRepository collectionRepository) public CollectionAuthorizationHandler(ICurrentContext currentContext, ICollectionRepository collectionRepository,
IFeatureService featureService)
{ {
_currentContext = currentContext; _currentContext = currentContext;
_collectionRepository = collectionRepository; _collectionRepository = collectionRepository;
_featureService = featureService;
} }
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
CollectionOperationRequirement requirement, ICollection<Collection> resources) CollectionOperationRequirement requirement, ICollection<Collection> resources)
{ {
if (!_featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext))
{
// Flexible collections is OFF, should not be using this handler
throw new FeatureUnavailableException("Flexible collections is OFF when it should be ON.");
}
// Establish pattern of authorization handler null checking passed resources // Establish pattern of authorization handler null checking passed resources
if (resources == null || !resources.Any()) if (resources == null || !resources.Any())
{ {

View File

@ -41,6 +41,7 @@ public static class FeatureFlagKeys
public const string AutofillV2 = "autofill-v2"; public const string AutofillV2 = "autofill-v2";
public const string BrowserFilelessImport = "browser-fileless-import"; public const string BrowserFilelessImport = "browser-fileless-import";
public const string FlexibleCollections = "flexible-collections"; public const string FlexibleCollections = "flexible-collections";
public const string BulkCollectionAccess = "bulk-collection-access";
public static List<string> GetAllKeys() public static List<string> GetAllKeys()
{ {