1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-15 01:58:14 -05:00

Moved authz code from GET organizations/orgId/users/id/groups to auth handler. Added tests.

This commit is contained in:
jrmccannon 2025-03-13 12:23:05 -05:00
parent 589af12f8f
commit 058460cec7
No known key found for this signature in database
GPG Key ID: CF03F3DB01CE96A6
5 changed files with 113 additions and 4 deletions

View File

@ -8,6 +8,7 @@ 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.Authorization.OrganizationUserGroups;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization;
using Bit.Core.AdminConsole.Repositories;
@ -186,11 +187,18 @@ public class OrganizationUsersController : Controller
}
[HttpGet("{id}/groups")]
public async Task<IEnumerable<string>> GetGroups(string orgId, string id)
public async Task<IEnumerable<string>> GetGroups([FromRoute] Guid orgId, [FromRoute] Guid id)
{
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
if (organizationUser == null || (!await _currentContext.ManageGroups(organizationUser.OrganizationId) &&
!await _currentContext.ManageUsers(organizationUser.OrganizationId)))
var authorized = await _authorizationService.AuthorizeAsync(User, new OrganizationScope(orgId),
[OrganizationUserGroupOperations.ReadAllIds]);
if (authorized.Succeeded is false)
{
throw new NotFoundException();
}
var organizationUser = await _organizationUserRepository.GetByIdAsync(id);
if (organizationUser == null)
{
throw new NotFoundException();
}

View File

@ -0,0 +1,10 @@
using Microsoft.AspNetCore.Authorization.Infrastructure;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization.OrganizationUserGroups;
public class OrganizationUserGroupOperationRequirement : OperationAuthorizationRequirement;
public static class OrganizationUserGroupOperations
{
public static readonly OrganizationUserGroupOperationRequirement ReadAllIds = new() { Name = nameof(ReadAllIds) };
}

View File

@ -0,0 +1,32 @@
using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization;
using Bit.Core.Context;
using Microsoft.AspNetCore.Authorization;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization.OrganizationUserGroups;
public class OrganizationUserGroupsAuthorizationHandler(ICurrentContext currentContext)
: AuthorizationHandler<OrganizationUserGroupOperationRequirement, OrganizationScope>
{
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
OrganizationUserGroupOperationRequirement requirement,
OrganizationScope resource)
{
var authorized = requirement switch
{
not null when requirement.Name == nameof(OrganizationUserGroupOperations.ReadAllIds) =>
await CanReadGroupIdsAsync(resource),
_ => false
};
if (authorized)
{
context.Succeed(requirement!);
return;
}
context.Fail();
}
private async Task<bool> CanReadGroupIdsAsync(OrganizationScope organizationId) =>
await currentContext.ManageUsers(organizationId) || await currentContext.ManageGroups(organizationId);
}

View File

@ -13,6 +13,7 @@ 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.Authorization.OrganizationUserGroups;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Models.Business.Tokenables;
using Bit.Core.OrganizationFeatures.OrganizationCollections;
@ -171,6 +172,8 @@ public static class OrganizationServiceCollectionExtensions
services.AddScoped<IAuthorizationHandler, OrganizationUserUserMiniDetailsAuthorizationHandler>();
services.AddScoped<IAuthorizationHandler, OrganizationUserUserDetailsAuthorizationHandler>();
services.AddScoped<IAuthorizationHandler, OrganizationUserDetailsAuthorizationHandler>();
services.AddScoped<IAuthorizationHandler, OrganizationUserGroupsAuthorizationHandler>();
services.AddScoped<IHasConfirmedOwnersExceptQuery, HasConfirmedOwnersExceptQuery>();
}

View File

@ -0,0 +1,56 @@
using System.Security.Claims;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization.OrganizationUserGroups;
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 OrganizationUserGroupsAuthorizationHandlerTests
{
[Theory]
[BitAutoData(true, false)]
[BitAutoData(false, true)]
[BitAutoData(true, true)]
public async Task ReadAllIds_UserCanManageUsersOrGroups_ShouldReturnSuccess(
bool canManageUsers,
bool canManageGroups,
CurrentContextOrganization contextOrganization,
SutProvider<OrganizationUserGroupsAuthorizationHandler> sutProvider)
{
sutProvider.GetDependency<ICurrentContext>().ManageUsers(contextOrganization.Id).Returns(canManageUsers);
sutProvider.GetDependency<ICurrentContext>().ManageGroups(contextOrganization.Id).Returns(canManageGroups);
var context = new AuthorizationHandlerContext(
[OrganizationUserGroupOperations.ReadAllIds],
new ClaimsPrincipal(),
new OrganizationScope(contextOrganization.Id));
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory]
[BitAutoData]
public async Task ReadAllIds_UserCannotManageUsersNorGroups_ShouldReturnFailure(CurrentContextOrganization contextOrganization,
SutProvider<OrganizationUserGroupsAuthorizationHandler> sutProvider)
{
sutProvider.GetDependency<ICurrentContext>().ManageUsers(contextOrganization.Id).Returns(false);
sutProvider.GetDependency<ICurrentContext>().ManageGroups(contextOrganization.Id).Returns(false);
var context = new AuthorizationHandlerContext(
[OrganizationUserGroupOperations.ReadAllIds],
new ClaimsPrincipal(),
new OrganizationScope(contextOrganization.Id));
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasFailed);
}
}