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

Merge branch 'master' into flexible-collections/deprecate-custom-collection-perm

# Conflicts:
#	src/Api/AdminConsole/Controllers/OrganizationUsersController.cs
#	src/Api/Vault/AuthorizationHandlers/Collections/CollectionAuthorizationHandler.cs
#	test/Api.Test/Vault/AuthorizationHandlers/CollectionAuthorizationHandlerTests.cs
This commit is contained in:
Rui Tome
2023-11-23 16:36:52 +00:00
191 changed files with 3610 additions and 4080 deletions

View File

@ -1,7 +1,10 @@
using Bit.Api.AdminConsole.Controllers;
using Bit.Api.AdminConsole.Models.Request.Organizations;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Entities;
using Bit.Core.Models.Data.Organizations.Policies;
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Services;
@ -82,7 +85,7 @@ public class OrganizationUsersControllerTests
};
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(orgId,
Core.Enums.PolicyType.ResetPassword).Returns(policy);
PolicyType.ResetPassword).Returns(policy);
await sutProvider.Sut.Accept(orgId, orgUserId, model);

View File

@ -1,9 +1,10 @@
using Bit.Api.Auth.Controllers;
using Bit.Api.Auth.Models.Request.Accounts;
using Bit.Api.Auth.Models.Request.Webauthn;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.Tokens;

View File

@ -2,6 +2,7 @@
using Bit.Api.Auth.Models.Request.Accounts;
using Bit.Api.Controllers;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Models.Api.Request.Accounts;
using Bit.Core.Auth.Services;
using Bit.Core.Auth.UserFeatures.UserKey;

View File

@ -1,10 +1,12 @@
using System.Security.Claims;
using System.Text.Json;
using Bit.Api.AdminConsole.Controllers;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data.Organizations.Policies;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Test.Common.AutoFixture;

View File

@ -21,481 +21,6 @@ namespace Bit.Api.Test.Vault.AuthorizationHandlers;
[FeatureServiceCustomize(FeatureFlagKeys.FlexibleCollections)]
public class BulkCollectionAuthorizationHandlerTests
{
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User, false, false)]
[BitAutoData(OrganizationUserType.Admin, false, true)]
[BitAutoData(OrganizationUserType.Owner, false, true)]
[BitAutoData(OrganizationUserType.Custom, true, true)]
public async Task CanCreateAsync_Success(
OrganizationUserType userType, bool createNewCollection, bool limitCollectionCreateDelete,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions
{
CreateNewCollections = createNewCollection
};
organization.LimitCollectionCreationDeletion = limitCollectionCreateDelete;
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Create },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanCreateAsync_WhenProviderUser_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions
{
CreateNewCollections = false
};
organization.LimitCollectionCreationDeletion = true;
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Create },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organization.Id).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanCreateAsync_WhenMissingCreateCollectionPermission_Failure(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions
{
CreateNewCollections = false
};
organization.LimitCollectionCreationDeletion = true;
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Create },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
public async Task CanReadAsync_WhenAdminOrOwner_Success(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Read },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanReadAsync_WhenProviderUser_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Read },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organization.Id).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(true, false)]
[BitAutoData(false, true)]
public async Task CanReadAsync_WhenCustomUserWithRequiredPermissions_Success(
bool editAnyCollection, bool deleteAnyCollection,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.Custom;
organization.Permissions = new Permissions
{
EditAnyCollection = editAnyCollection,
DeleteAnyCollection = deleteAnyCollection
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Read },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanReadAsync_WhenUserAssigned_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Read },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanReadAsync_WhenMissingAccess_Failure(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions
{
EditAnyCollection = false,
DeleteAnyCollection = false
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Read },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
public async Task CanReadAccessAsync_WhenAdminOrOwner_Success(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanReadAccessAsync_WhenProviderUser_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organization.Id).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(true, false)]
[BitAutoData(false, true)]
public async Task CanReadAccessAsync_WhenCustomUserWithRequiredPermissions_Success(
bool editAnyCollection, bool deleteAnyCollection,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.Custom;
organization.Permissions = new Permissions
{
EditAnyCollection = editAnyCollection,
DeleteAnyCollection = deleteAnyCollection
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanReadAccessAsync_WhenUserAssigned_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanReadAccessAsync_WhenMissingAccess_Failure(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions
{
EditAnyCollection = false,
DeleteAnyCollection = false
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User, false, true)]
[BitAutoData(OrganizationUserType.Admin, false, false)]
[BitAutoData(OrganizationUserType.Owner, false, false)]
[BitAutoData(OrganizationUserType.Custom, true, false)]
[BitAutoData(OrganizationUserType.Owner, true, true)]
public async Task CanUpdateAsync_Success(
OrganizationUserType userType, bool editAnyCollection, bool manageCollections,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
ICollection<CollectionDetails> collectionDetails,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
foreach (var collectionDetail in collectionDetails)
{
collectionDetail.Manage = manageCollections;
}
organization.Type = userType;
organization.Permissions = new Permissions
{
EditAnyCollection = editAnyCollection
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Update },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanUpdateAsync_WhenProviderUser_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Update },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organization.Id).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanUpdateAsync_WhenMissingManageCollectionPermission_Failure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
ICollection<CollectionDetails> collectionDetails,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
foreach (var collectionDetail in collectionDetails)
{
collectionDetail.Manage = true;
}
// Simulate one collection missing the manage permission
collectionDetails.First().Manage = false;
// Ensure the user is not an owner/admin and does not have edit any collection permission
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions
{
EditAnyCollection = false
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Update },
new ClaimsPrincipal(),
collections
);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
sutProvider.GetDependency<ICurrentContext>().ReceivedWithAnyArgs().GetOrganization(default);
await sutProvider.GetDependency<ICollectionRepository>().ReceivedWithAnyArgs()
.GetManyByUserIdAsync(default);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User, false, true)]
[BitAutoData(OrganizationUserType.Admin, false, false)]
@ -516,10 +41,7 @@ public class BulkCollectionAuthorizationHandlerTests
}
organization.Type = userType;
organization.Permissions = new Permissions
{
EditAnyCollection = editAnyCollection
};
organization.Permissions.EditAnyCollection = editAnyCollection;
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ModifyAccess },
@ -535,71 +57,36 @@ public class BulkCollectionAuthorizationHandlerTests
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanManageCollectionAccessAsync_WhenProviderUser_Success(
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User, false, false)]
[BitAutoData(OrganizationUserType.Admin, false, true)]
[BitAutoData(OrganizationUserType.Owner, false, true)]
[BitAutoData(OrganizationUserType.Custom, true, true)]
public async Task CanCreateAsync_Success(
OrganizationUserType userType, bool createNewCollection, bool limitCollectionCreateDelete,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
organization.Type = userType;
organization.Permissions.CreateNewCollections = createNewCollection;
organization.LimitCollectionCreationDeletion = limitCollectionCreateDelete;
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ModifyAccess },
new[] { CollectionOperations.Create },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organization.Id).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanManageCollectionAccessAsync_WhenMissingManageCollectionPermission_Failure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
ICollection<CollectionDetails> collectionDetails,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
foreach (var collectionDetail in collectionDetails)
{
collectionDetail.Manage = true;
}
// Simulate one collection missing the manage permission
collectionDetails.First().Manage = false;
// Ensure the user is not an owner/admin and does not have edit any collection permission
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions
{
EditAnyCollection = false
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ModifyAccess },
new ClaimsPrincipal(),
collections
);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
sutProvider.GetDependency<ICurrentContext>().ReceivedWithAnyArgs().GetOrganization(default);
await sutProvider.GetDependency<ICollectionRepository>().ReceivedWithAnyArgs()
.GetManyByUserIdAsync(default);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User, false, false, true)]
[BitAutoData(OrganizationUserType.Admin, false, true, false)]
@ -619,10 +106,7 @@ public class BulkCollectionAuthorizationHandlerTests
}
organization.Type = userType;
organization.Permissions = new Permissions
{
DeleteAnyCollection = deleteAnyCollection
};
organization.Permissions.DeleteAnyCollection = deleteAnyCollection;
organization.LimitCollectionCreationDeletion = limitCollectionCreateDelete;
var context = new AuthorizationHandlerContext(
@ -639,125 +123,6 @@ public class BulkCollectionAuthorizationHandlerTests
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WithProviderUser_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions
{
DeleteAnyCollection = false
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organization.Id).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WhenCustomUserWithRequiredPermissions_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.Custom;
organization.Permissions = new Permissions
{
DeleteAnyCollection = true
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanDeleteAsync_WhenMissingPermissions_Failure(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions
{
DeleteAnyCollection = false
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_NullResource_NoSuccessOrFailure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider)
{
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Create },
new ClaimsPrincipal(),
null
);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
Assert.False(context.HasFailed);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_EmptyResourceList_NoSuccessOrFailure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider)
{
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Create },
new ClaimsPrincipal(),
new List<Collection>()
);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(new Guid());
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
Assert.False(context.HasFailed);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_MissingUserId_Failure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
@ -778,7 +143,7 @@ public class BulkCollectionAuthorizationHandlerTests
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_TargetCollectionsMultipleOrgs_Failure(
public async Task HandleRequirementAsync_TargetCollectionsMultipleOrgs_Exception(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
IList<Collection> collections)
{
@ -801,7 +166,7 @@ public class BulkCollectionAuthorizationHandlerTests
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_MissingOrg_Failure(
public async Task HandleRequirementAsync_MissingOrg_NoSuccess(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections)
{
@ -817,6 +182,76 @@ public class BulkCollectionAuthorizationHandlerTests
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns((CurrentContextOrganization)null);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasFailed);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_Provider_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections)
{
var operationsToTest = new[]
{
CollectionOperations.Create, CollectionOperations.Delete, CollectionOperations.ModifyAccess
};
foreach (var op in operationsToTest)
{
var actingUserId = Guid.NewGuid();
var context = new AuthorizationHandlerContext(
new[] { op },
new ClaimsPrincipal(),
collections
);
var orgId = collections.First().OrganizationId;
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(orgId).Returns((CurrentContextOrganization)null);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
await sutProvider.GetDependency<ICurrentContext>().Received().ProviderUserForOrgAsync(orgId);
// Recreate the SUT to reset the mocks/dependencies between tests
sutProvider.Recreate();
}
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanManageCollectionAccessAsync_MissingManageCollectionPermission_NoSuccess(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
ICollection<CollectionDetails> collectionDetails,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
foreach (var collectionDetail in collectionDetails)
{
collectionDetail.Manage = true;
}
// Simulate one collection missing the manage permission
collectionDetails.First().Manage = false;
// Ensure the user is not an owner/admin and does not have edit any collection permission
organization.Type = OrganizationUserType.User;
organization.Permissions.EditAnyCollection = false;
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ModifyAccess },
new ClaimsPrincipal(),
collections
);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
sutProvider.GetDependency<ICurrentContext>().ReceivedWithAnyArgs().GetOrganization(default);
await sutProvider.GetDependency<ICollectionRepository>().ReceivedWithAnyArgs()
.GetManyByUserIdAsync(default);
}
}

View File

@ -3,6 +3,7 @@ using System.Text.Json;
using AutoFixture;
using Bit.Api.Vault.Controllers;
using Bit.Api.Vault.Models.Response;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Models.Data.Provider;
using Bit.Core.AdminConsole.Repositories;

View File

@ -374,6 +374,24 @@
"Microsoft.Win32.Registry": "5.0.0"
}
},
"Duende.IdentityServer": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "4HVjzx1F8v5J+U7oa8RGAQGj2QzmzNSu87r18Sh+dlh10uyZZL8teAaT/FaVLDObnfItGdPFvN8mwpF/HkI3Xw==",
"dependencies": {
"Duende.IdentityServer.Storage": "6.0.4",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "6.0.0"
}
},
"Duende.IdentityServer.Storage": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "s5gAjfbpr2IMgI+fU2Nx+2AZdzstmbt9gpo13iX7GwvqSeSaBVqj9ZskAN0R2KF1OemPdZuGnfaTcevdXMUrrw==",
"dependencies": {
"IdentityModel": "6.0.0",
"Microsoft.AspNetCore.DataProtection.Abstractions": "6.0.0"
}
},
"Fare": {
"type": "Transitive",
"resolved": "2.1.1",
@ -418,49 +436,8 @@
},
"IdentityModel": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "b18wrIx5wnZlMxAX7oVsE+nDtAJ4hajYlH0xPlaRvo4r/fz08K6pPeZvbiqS9nfNbzfIgLFmNX+FL9qR9ZR5PA==",
"dependencies": {
"Newtonsoft.Json": "11.0.2",
"System.Text.Encodings.Web": "4.7.0"
}
},
"IdentityModel.AspNetCore.OAuth2Introspection": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "ZNdMZMaj9fqR3j50vYsu+1U3QGd6n8+fqwf+a8mCTcmXGor+HgFDfdq0mM34bsmD6uEgAQup7sv2ZW5kR36dbA==",
"dependencies": {
"IdentityModel": "4.0.0"
}
},
"IdentityServer4": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "blaxxGuOA7v/w1q+fxn97wZ+x2ecG1ZD4mc/N/ZOXMNeFZZhqv+4LF26Gecyik3nWrJPmbMEtQbLmRsKG8k61w==",
"dependencies": {
"IdentityModel": "4.4.0",
"IdentityServer4.Storage": "4.1.2",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "3.1.0",
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.6.0",
"Newtonsoft.Json": "12.0.2"
}
},
"IdentityServer4.AccessTokenValidation": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "qu/M6UyN4o9NVep7q545Ms7hYAnsQqSdLbN1Fjjrn4m35lyBfeQPSSNzDryAKHbodyWOQfHaOqKEyMEJQ5Rpgw==",
"dependencies": {
"IdentityModel.AspNetCore.OAuth2Introspection": "4.0.1",
"Microsoft.AspNetCore.Authentication.JwtBearer": "3.0.0"
}
},
"IdentityServer4.Storage": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "KoSffyZyyeCNTIyJiZnCuPakJ1QbCHlpty6gbWUj/7yl+w0PXIchgmmJnJSvddzBb8iZ2xew/vGlxWUIP17P2g==",
"dependencies": {
"IdentityModel": "4.4.0"
}
"resolved": "6.0.0",
"contentHash": "eVHCR7a6m/dm5RFcBzE3qs/Jg5j9R5Rjpu8aTOv9e4AFvaQtBXb5ah7kmwU+YwA0ufRwz4wf1hnIvsD2hSnI4g=="
},
"Kralizek.AutoFixture.Extensions.MockHttp": {
"type": "Transitive",
@ -561,10 +538,10 @@
},
"Microsoft.AspNetCore.Authentication.OpenIdConnect": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "O1cAQYUTU8EfRqwc5/rfTns4E4hKlFlg59fuKRrST+PzsxI6H07KqRN/JjdYhAuVYxF8jPnIGbj+zuc5paOWUw==",
"resolved": "6.0.0",
"contentHash": "cJxdro36spFzk/K2OFCddM6vZ+yoj6ug8mTFRH3Gdv1Pul/buSuCtfb/FSCp31UmS5S4C1315dU7wX3ErLFuDg==",
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.5.0"
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.10.0"
}
},
"Microsoft.AspNetCore.Cryptography.Internal": {
@ -597,8 +574,8 @@
},
"Microsoft.AspNetCore.DataProtection.Abstractions": {
"type": "Transitive",
"resolved": "3.1.32",
"contentHash": "MPL4iVyiaRxnOUY5VATHjvhDWaAEFb77KFiUxVRklv3Z3v+STofUr1UG/aCt1O9cgN7FVTDaC5A7U+zsLub8Xg=="
"resolved": "6.0.0",
"contentHash": "Z/UU4NEBm5UgNufJmw+j5baW26ytCOIZ0G7sZocPaOzsUeBon1bkM3lSMNZQG2GmDjAIVP2XMSODf2jzSGbibw=="
},
"Microsoft.Azure.Amqp": {
"type": "Transitive",
@ -3031,25 +3008,25 @@
"AspNetCore.HealthChecks.SqlServer": "[6.0.2, )",
"AspNetCore.HealthChecks.Uris": "[6.0.3, )",
"Azure.Messaging.EventGrid": "[4.10.0, )",
"Commercial.Core": "[2023.10.2, )",
"Commercial.Infrastructure.EntityFramework": "[2023.10.2, )",
"Core": "[2023.10.2, )",
"SharedWeb": "[2023.10.2, )",
"Commercial.Core": "[2023.10.3, )",
"Commercial.Infrastructure.EntityFramework": "[2023.10.3, )",
"Core": "[2023.10.3, )",
"SharedWeb": "[2023.10.3, )",
"Swashbuckle.AspNetCore": "[6.5.0, )"
}
},
"commercial.core": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )"
"Core": "[2023.10.3, )"
}
},
"commercial.infrastructure.entityframework": {
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.10.2, )",
"Infrastructure.EntityFramework": "[2023.10.2, )"
"Core": "[2023.10.3, )",
"Infrastructure.EntityFramework": "[2023.10.3, )"
}
},
"common": {
@ -3057,7 +3034,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
@ -3079,10 +3056,9 @@
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.19.0, )",
"DnsClient": "[1.7.0, )",
"Duende.IdentityServer": "[6.0.4, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[8.0.0, )",
"MailKit": "[4.2.0, )",
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
@ -3112,8 +3088,8 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Common": "[2023.10.2, )",
"Core": "[2023.10.2, )",
"Common": "[2023.10.3, )",
"Core": "[2023.10.3, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
@ -3123,7 +3099,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Dapper": "[2.0.123, )"
}
},
@ -3131,7 +3107,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )",
@ -3143,9 +3119,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Infrastructure.Dapper": "[2023.10.2, )",
"Infrastructure.EntityFramework": "[2023.10.2, )"
"Core": "[2023.10.3, )",
"Infrastructure.Dapper": "[2023.10.3, )",
"Infrastructure.EntityFramework": "[2023.10.3, )"
}
}
}