Update OrganizationUserControllerPutTests and CollectionUserAuthorizationHandlerTests to throw BadRequestException for self-assignment attempts. Enhance unit tests to validate new exception handling and authorization logic for user and group operations.

This commit is contained in:
Rui Tome
2026-04-10 16:28:40 +01:00
parent 851919f211
commit 81dae3ed9a
3 changed files with 220 additions and 8 deletions

View File

@@ -18,6 +18,7 @@ using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Microsoft.AspNetCore.Authorization;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using Xunit;
namespace Bit.Api.Test.AdminConsole.Controllers;
@@ -358,7 +359,7 @@ public class OrganizationUserControllerPutTests
[Theory]
[BitAutoData]
public async Task Put_FeatureFlagEnabled_SelfAssignment_ThrowsNotFound(OrganizationUserUpdateRequestModel model,
public async Task Put_FeatureFlagEnabled_SelfAssignment_ThrowsBadRequest(OrganizationUserUpdateRequestModel model,
OrganizationUser organizationUser, OrganizationAbility organizationAbility,
SutProvider<OrganizationUsersController> sutProvider, Guid savingUserId)
{
@@ -370,14 +371,15 @@ public class OrganizationUserControllerPutTests
.IsEnabled(FeatureFlagKeys.CollectionUserCollectionGroupAuthorizationHandlers)
.Returns(true);
// Self-assignment check is now in the handler; when it denies, the extension throws NotFoundException
// Self-assignment check in handler throws BadRequestException directly
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Any<CollectionUserAccessResource>(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs => reqs.Contains(CollectionUserOperations.Create)))
.Returns(AuthorizationResult.Failed());
.Throws(new BadRequestException("You cannot add yourself to a collection."));
await Assert.ThrowsAsync<NotFoundException>(() =>
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.Put(organizationAbility.Id, organizationUser.Id, model));
Assert.Equal("You cannot add yourself to a collection.", exception.Message);
}
[Theory]

View File

@@ -579,6 +579,128 @@ public class CollectionGroupAuthorizationHandlerTests
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanUpdate_WithCustomUserManageGroupsAndNoAllowAdminAccess_DoesNotSucceed(
SutProvider<CollectionGroupAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization,
Guid actingUserId)
{
organization.Type = OrganizationUserType.Custom;
organization.Permissions = new Permissions { ManageGroups = true };
var resources = collections.ToList();
ArrangeFeatureFlag(sutProvider);
ArrangeOrganizationAbility(sutProvider, organization, false);
var context = new AuthorizationHandlerContext(
new[] { CollectionGroupOperations.Update },
new ClaimsPrincipal(),
resources);
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 CanUpdate_WithAdminOrOwnerAndNoAllowAdminAccess_DoesNotSucceed(
OrganizationUserType userType,
Guid actingUserId,
SutProvider<CollectionGroupAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
organization.Type = userType;
organization.Permissions = new Permissions();
var resources = collections.ToList();
ArrangeFeatureFlag(sutProvider);
ArrangeOrganizationAbility(sutProvider, organization, false);
var context = new AuthorizationHandlerContext(
new[] { CollectionGroupOperations.Update },
new ClaimsPrincipal(),
resources);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>())
.Returns(false);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDelete_WithCustomUserManageGroupsAndNoAllowAdminAccess_DoesNotSucceed(
SutProvider<CollectionGroupAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization,
Guid actingUserId)
{
organization.Type = OrganizationUserType.Custom;
organization.Permissions = new Permissions { ManageGroups = true };
var resources = collections.ToList();
ArrangeFeatureFlag(sutProvider);
ArrangeOrganizationAbility(sutProvider, organization, false);
var context = new AuthorizationHandlerContext(
new[] { CollectionGroupOperations.Delete },
new ClaimsPrincipal(),
resources);
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 CanDelete_WithAdminOrOwnerAndNoAllowAdminAccess_DoesNotSucceed(
OrganizationUserType userType,
Guid actingUserId,
SutProvider<CollectionGroupAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
organization.Type = userType;
organization.Permissions = new Permissions();
var resources = collections.ToList();
ArrangeFeatureFlag(sutProvider);
ArrangeOrganizationAbility(sutProvider, organization, false);
var context = new AuthorizationHandlerContext(
new[] { CollectionGroupOperations.Delete },
new ClaimsPrincipal(),
resources);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>())
.Returns(false);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
private static void ArrangeFeatureFlag(SutProvider<CollectionGroupAuthorizationHandler> sutProvider)
{
sutProvider.GetDependency<IFeatureService>()

View File

@@ -214,7 +214,7 @@ public class CollectionUserAuthorizationHandlerTests
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
public async Task CanCreate_WithSelfAssignmentAndNoAllowAdminAccess_DoesNotSucceed(
public async Task CanCreate_WithSelfAssignmentAndNoAllowAdminAccess_ThrowsBadRequest(
OrganizationUserType userType,
Guid actingUserId,
SutProvider<CollectionUserAuthorizationHandler> sutProvider,
@@ -235,9 +235,9 @@ public class CollectionUserAuthorizationHandlerTests
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.HandleAsync(context));
Assert.Equal("You cannot add yourself to a collection.", exception.Message);
}
[Theory, CollectionCustomization]
@@ -657,6 +657,94 @@ public class CollectionUserAuthorizationHandlerTests
Assert.False(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
public async Task CanUpdate_WithAdminOrOwnerAndNoAllowAdminAccess_DoesNotSucceed(
OrganizationUserType userType,
Guid actingUserId,
SutProvider<CollectionUserAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
organization.Type = userType;
organization.Permissions = new Permissions();
ArrangeFeatureFlag(sutProvider);
ArrangeOrganizationAbility(sutProvider, organization, false);
var context = new AuthorizationHandlerContext(
new[] { CollectionUserOperations.Update },
new ClaimsPrincipal(),
MakeResource(collections));
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>())
.Returns(false);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDelete_WithCustomUserManageUsersAndNoAllowAdminAccess_DoesNotSucceed(
SutProvider<CollectionUserAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization,
Guid actingUserId)
{
organization.Type = OrganizationUserType.Custom;
organization.Permissions = new Permissions { ManageUsers = true };
ArrangeFeatureFlag(sutProvider);
ArrangeOrganizationAbility(sutProvider, organization, false);
var context = new AuthorizationHandlerContext(
new[] { CollectionUserOperations.Delete },
new ClaimsPrincipal(),
MakeResource(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 CanDelete_WithAdminOrOwnerAndNoAllowAdminAccess_DoesNotSucceed(
OrganizationUserType userType,
Guid actingUserId,
SutProvider<CollectionUserAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
organization.Type = userType;
organization.Permissions = new Permissions();
ArrangeFeatureFlag(sutProvider);
ArrangeOrganizationAbility(sutProvider, organization, false);
var context = new AuthorizationHandlerContext(
new[] { CollectionUserOperations.Delete },
new ClaimsPrincipal(),
MakeResource(collections));
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>())
.Returns(false);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
private static CollectionUserAccessResource MakeResource<T>(
ICollection<T> collections, Guid? targetUserId = null) where T : Collection
{