mirror of
https://github.com/bitwarden/server.git
synced 2025-05-07 04:32:20 -05:00
Moved auth logic for GET /organizations/{orgId}/users/{id} to auth handler. Updated tests as well.
This commit is contained in:
parent
d40fbe3217
commit
589af12f8f
@ -7,6 +7,7 @@ using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization.OrganizationUserDetails;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
@ -107,10 +108,18 @@ public class OrganizationUsersController : Controller
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<OrganizationUserDetailsResponseModel> Get(Guid id, bool includeGroups = false)
|
||||
public async Task<OrganizationUserDetailsResponseModel> Get([FromRoute] Guid orgId, Guid id, bool includeGroups = false)
|
||||
{
|
||||
var authorizationResult = await _authorizationService.AuthorizeAsync(User, new OrganizationScope(orgId),
|
||||
OrganizationUserDetailsOperations.Read);
|
||||
|
||||
if (authorizationResult.Succeeded is false)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var (organizationUser, collections) = await _organizationUserRepository.GetDetailsByIdWithCollectionsAsync(id);
|
||||
if (organizationUser == null || !await _currentContext.ManageUsers(organizationUser.OrganizationId))
|
||||
if (organizationUser == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
@ -707,7 +716,7 @@ public class OrganizationUsersController : Controller
|
||||
{
|
||||
if (!_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
|
||||
{
|
||||
return userIds.ToDictionary(kvp => kvp, kvp => false);
|
||||
return userIds.ToDictionary(kvp => kvp, _ => false);
|
||||
}
|
||||
|
||||
var usersOrganizationManagementStatus = await _getOrganizationUsersManagementStatusQuery.GetUsersOrganizationManagementStatusAsync(orgId, userIds);
|
||||
|
@ -0,0 +1,30 @@
|
||||
#nullable enable
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization;
|
||||
using Bit.Core.Context;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization.OrganizationUserDetails;
|
||||
|
||||
public class OrganizationUserDetailsAuthorizationHandler(ICurrentContext currentContext) :
|
||||
AuthorizationHandler<OrganizationUserDetailsOperationRequirement, OrganizationScope>
|
||||
{
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
|
||||
OrganizationUserDetailsOperationRequirement requirement,
|
||||
OrganizationScope organizationScope)
|
||||
{
|
||||
var authorized = requirement switch
|
||||
{
|
||||
not null when requirement.Name == nameof(OrganizationUserDetailsOperations.Read) => await currentContext
|
||||
.ManageUsers(organizationScope),
|
||||
_ => false
|
||||
};
|
||||
|
||||
if (authorized)
|
||||
{
|
||||
context.Succeed(requirement!);
|
||||
return;
|
||||
}
|
||||
|
||||
context.Fail();
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
using Microsoft.AspNetCore.Authorization.Infrastructure;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization.OrganizationUserDetails;
|
||||
|
||||
public class OrganizationUserDetailsOperationRequirement : OperationAuthorizationRequirement;
|
||||
|
||||
public static class OrganizationUserDetailsOperations
|
||||
{
|
||||
public static readonly OrganizationUserDetailsOperationRequirement Read = new() { Name = nameof(Read) };
|
||||
}
|
@ -12,6 +12,7 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization.OrganizationUserDetails;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.Models.Business.Tokenables;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationCollections;
|
||||
@ -169,6 +170,7 @@ public static class OrganizationServiceCollectionExtensions
|
||||
|
||||
services.AddScoped<IAuthorizationHandler, OrganizationUserUserMiniDetailsAuthorizationHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, OrganizationUserUserDetailsAuthorizationHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, OrganizationUserDetailsAuthorizationHandler>();
|
||||
services.AddScoped<IHasConfirmedOwnersExceptQuery, HasConfirmedOwnersExceptQuery>();
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,9 @@ using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization.OrganizationUserDetails;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Auth.Repositories;
|
||||
@ -250,9 +252,13 @@ public class OrganizationUsersControllerTests
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(accountDeprovisioningEnabled);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.ManageUsers(organizationUser.OrganizationId)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<IAuthorizationService>()
|
||||
.AuthorizeAsync(
|
||||
user: Arg.Any<ClaimsPrincipal>(),
|
||||
resource: Arg.Is<OrganizationScope>(x => x == organizationUser.OrganizationId),
|
||||
requirements: Arg.Is<IEnumerable<IAuthorizationRequirement>>(x =>
|
||||
x.Any(y => y == OrganizationUserDetailsOperations.Read)))
|
||||
.Returns(AuthorizationResult.Success());
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetDetailsByIdWithCollectionsAsync(organizationUser.Id)
|
||||
@ -262,7 +268,7 @@ public class OrganizationUsersControllerTests
|
||||
.GetUsersOrganizationManagementStatusAsync(organizationUser.OrganizationId, Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(organizationUser.Id)))
|
||||
.Returns(new Dictionary<Guid, bool> { { organizationUser.Id, true } });
|
||||
|
||||
var response = await sutProvider.Sut.Get(organizationUser.Id, false);
|
||||
var response = await sutProvider.Sut.Get(organizationUser.OrganizationId, organizationUser.Id, false);
|
||||
|
||||
Assert.Equal(organizationUser.Id, response.Id);
|
||||
Assert.Equal(accountDeprovisioningEnabled, response.ManagedByOrganization);
|
||||
|
@ -0,0 +1,49 @@
|
||||
using System.Security.Claims;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization.OrganizationUserDetails;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.Authorization;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class OrganizationUserDetailsAuthorizationHandlerTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Read_UserCanManageUsers_ShouldReturnSuccess(CurrentContextOrganization contextOrganization,
|
||||
SutProvider<OrganizationUserDetailsAuthorizationHandler> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(contextOrganization.Id).Returns(true);
|
||||
|
||||
var context = new AuthorizationHandlerContext(
|
||||
[OrganizationUserDetailsOperations.Read],
|
||||
new ClaimsPrincipal(),
|
||||
new OrganizationScope(contextOrganization.Id));
|
||||
|
||||
await sutProvider.Sut.HandleAsync(context);
|
||||
|
||||
Assert.True(context.HasSucceeded);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Read_UserCannotManageUsers_ShouldReturnFailure(CurrentContextOrganization contextOrganization,
|
||||
SutProvider<OrganizationUserDetailsAuthorizationHandler> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(contextOrganization.Id).Returns(false);
|
||||
|
||||
var context = new AuthorizationHandlerContext(
|
||||
[OrganizationUserDetailsOperations.Read],
|
||||
new ClaimsPrincipal(),
|
||||
new OrganizationScope(contextOrganization.Id));
|
||||
|
||||
await sutProvider.Sut.HandleAsync(context);
|
||||
|
||||
Assert.True(context.HasFailed);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user