mirror of
https://github.com/bitwarden/server.git
synced 2026-06-01 11:45:20 -05:00
* feat(admin-console): Add InjectOrganizationAttribute and OrganizationModelBinder for automatic organization parameter binding * feat(admin-console): Introduce BindOrganizationAttribute and OrganizationModelBinder for organization parameter binding with unit tests * feat(admin-console): Update GetResetPasswordDetails to use BindOrganization for organization parameter * fix(admin-console): Correct organization ID check in GetResetPasswordDetails method to use bound organization * Refactor OrganizationUsersControllerTests to use bound organization in GetResetPasswordDetails method - Updated test cases to pass the organization directly instead of relying on repository calls. - Ensured that the tests correctly assert NotFoundException when the organization user does not match the bound organization. - Improved clarity in test setup by explicitly binding the organization to the method calls. * Fix UTF-8 BOM issue in BindOrganizationAttribute.cs * Add integration tests for OrganizationUsersController's BindOrganization functionality - Introduced OrganizationUsersControllerBindOrganizationTests to validate the behavior of the GET reset-password-details endpoint. - Implemented tests for successful retrieval of reset password details, handling of non-existent organization users, and cases where the user belongs to a different organization. - Ensured comprehensive coverage of scenarios to verify correct status responses and organization binding logic.
123 lines
4.2 KiB
C#
123 lines
4.2 KiB
C#
using Bit.Api.AdminConsole.Attributes;
|
|
using Bit.Core.AdminConsole.Entities;
|
|
using Bit.Core.Exceptions;
|
|
using Bit.Core.Repositories;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|
using Microsoft.AspNetCore.Routing;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using NSubstitute;
|
|
using Xunit;
|
|
|
|
namespace Bit.Api.Test.AdminConsole.Attributes;
|
|
|
|
public class BindOrganizationAttributeTests
|
|
{
|
|
private readonly IOrganizationRepository _organizationRepository;
|
|
private readonly Organization _organization;
|
|
private readonly Guid _orgId;
|
|
|
|
public BindOrganizationAttributeTests()
|
|
{
|
|
_organizationRepository = Substitute.For<IOrganizationRepository>();
|
|
_orgId = Guid.NewGuid();
|
|
_organization = new Organization { Id = _orgId };
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BindModelAsync_OrganizationExists_BindsSuccessfully()
|
|
{
|
|
var binder = new OrganizationModelBinder();
|
|
_organizationRepository.GetByIdAsync(_orgId).Returns(_organization);
|
|
|
|
var context = CreateBindingContext();
|
|
|
|
await binder.BindModelAsync(context);
|
|
|
|
Assert.True(context.Result.IsModelSet);
|
|
Assert.Equal(_organization, context.Result.Model);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BindModelAsync_OrganizationNotFound_ThrowsNotFoundException()
|
|
{
|
|
var binder = new OrganizationModelBinder();
|
|
_organizationRepository.GetByIdAsync(_orgId).Returns((Organization)null);
|
|
|
|
var context = CreateBindingContext();
|
|
|
|
await Assert.ThrowsAsync<NotFoundException>(() => binder.BindModelAsync(context));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BindModelAsync_InvalidOrgId_ThrowsBadRequestException()
|
|
{
|
|
var binder = new OrganizationModelBinder();
|
|
var context = CreateBindingContext(orgIdRouteValue: "not-a-guid");
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => binder.BindModelAsync(context));
|
|
Assert.Equal("Route parameter 'orgId' or 'organizationId' is missing or invalid.", exception.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BindModelAsync_MissingOrgId_ThrowsBadRequestException()
|
|
{
|
|
var binder = new OrganizationModelBinder();
|
|
var context = CreateBindingContext(includeOrgId: false);
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => binder.BindModelAsync(context));
|
|
Assert.Equal("Route parameter 'orgId' or 'organizationId' is missing or invalid.", exception.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BindModelAsync_OrganizationIdRouteParam_ResolvesOrgId()
|
|
{
|
|
var binder = new OrganizationModelBinder();
|
|
_organizationRepository.GetByIdAsync(_orgId).Returns(_organization);
|
|
|
|
var context = CreateBindingContext(useOrganizationIdRoute: true);
|
|
|
|
await binder.BindModelAsync(context);
|
|
|
|
Assert.True(context.Result.IsModelSet);
|
|
Assert.Equal(_organization, context.Result.Model);
|
|
}
|
|
|
|
private DefaultModelBindingContext CreateBindingContext(
|
|
string orgIdRouteValue = null,
|
|
bool includeOrgId = true,
|
|
bool useOrganizationIdRoute = false)
|
|
{
|
|
var httpContext = new DefaultHttpContext();
|
|
var services = new ServiceCollection();
|
|
services.AddScoped(_ => _organizationRepository);
|
|
httpContext.RequestServices = services.BuildServiceProvider();
|
|
|
|
var routeData = new RouteData();
|
|
if (includeOrgId)
|
|
{
|
|
var key = useOrganizationIdRoute ? "organizationId" : "orgId";
|
|
routeData.Values[key] = orgIdRouteValue ?? _orgId.ToString();
|
|
}
|
|
|
|
httpContext.Request.RouteValues = routeData.Values;
|
|
|
|
var actionContext = new ActionContext(
|
|
httpContext,
|
|
routeData,
|
|
new Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor(),
|
|
new ModelStateDictionary());
|
|
|
|
var metadataProvider = new EmptyModelMetadataProvider();
|
|
var metadata = metadataProvider.GetMetadataForType(typeof(Organization));
|
|
|
|
return new DefaultModelBindingContext
|
|
{
|
|
ActionContext = actionContext,
|
|
ModelMetadata = metadata,
|
|
ModelName = "organization"
|
|
};
|
|
}
|
|
}
|