mirror of
https://github.com/bitwarden/server.git
synced 2025-04-05 05:00:19 -05:00
Use httpContext features for providers
This commit is contained in:
parent
48697c4900
commit
038e6e63b6
@ -1,6 +1,5 @@
|
||||
#nullable enable
|
||||
|
||||
using Bit.Api.AdminConsole.Context;
|
||||
using Bit.Core.Context;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
@ -15,7 +14,6 @@ namespace Bit.Api.AdminConsole.Authorization;
|
||||
public interface IOrganizationRequirement : IAuthorizationRequirement
|
||||
{
|
||||
public Task<bool> AuthorizeAsync(
|
||||
Guid organizationId,
|
||||
CurrentContextOrganization? organizationClaims,
|
||||
IProviderOrganizationContext providerOrganizationContext);
|
||||
Func<Task<bool>> isProviderUserForOrg);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Api.AdminConsole.Authorization;
|
||||
|
||||
public static class ClaimsExtensions
|
||||
public static class OrganizationClaimsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// A delegate that returns true if the user has the specified claim type for an organization, false otherwise.
|
@ -1,6 +1,9 @@
|
||||
#nullable enable
|
||||
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using BadRequestException = Bit.Core.Exceptions.BadRequestException;
|
||||
|
||||
namespace Bit.Api.AdminConsole.Authorization;
|
||||
|
||||
@ -10,17 +13,31 @@ namespace Bit.Api.AdminConsole.Authorization;
|
||||
/// determine whether the action is authorized.
|
||||
/// </summary>
|
||||
public class OrganizationRequirementHandler(
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IProviderUserRepository providerUserRepository,
|
||||
IUserService userService)
|
||||
: AuthorizationHandler<IOrganizationRequirement>
|
||||
{
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, IOrganizationRequirement requirement)
|
||||
{
|
||||
var organizationId = httpContextAccessor.GetOrganizationId();
|
||||
var httpContext = httpContextAccessor.HttpContext;
|
||||
if (httpContext == null)
|
||||
{
|
||||
throw new InvalidOperationException("This method should only be called in the context of an HTTP Request.");
|
||||
}
|
||||
|
||||
var organizationClaims = context.User.GetCurrentContextOrganization(organizationId);
|
||||
var providerOrganizationContext = null; // TODO
|
||||
var organizationId = httpContext.GetOrganizationId();
|
||||
var organizationClaims = httpContext.User.GetCurrentContextOrganization(organizationId);
|
||||
|
||||
var authorized = await requirement.AuthorizeAsync(organizationId, organizationClaims, providerOrganizationContext);
|
||||
var userId = userService.GetProperUserId(httpContext.User);
|
||||
if (userId == null)
|
||||
{
|
||||
throw new BadRequestException("This method should only be called on the private api with a logged in user.");
|
||||
}
|
||||
|
||||
Task<bool> IsProviderUserForOrg() => httpContextAccessor.HttpContext.IsProviderUserForOrgAsync(providerUserRepository, userId.Value, organizationId);
|
||||
|
||||
var authorized = await requirement.AuthorizeAsync(organizationClaims, IsProviderUserForOrg);
|
||||
|
||||
if (authorized)
|
||||
{
|
||||
|
@ -4,14 +4,9 @@ namespace Bit.Api.AdminConsole.Authorization;
|
||||
|
||||
public static class OrganizationRequirementHelpers
|
||||
{
|
||||
public static Guid GetOrganizationId(this IHttpContextAccessor httpContextAccessor)
|
||||
public static Guid GetOrganizationId(this HttpContext httpContext)
|
||||
{
|
||||
if (httpContextAccessor.HttpContext is null)
|
||||
{
|
||||
throw new InvalidOperationException("This method should only be called in the context of an HTTP Request.");
|
||||
}
|
||||
|
||||
httpContextAccessor.HttpContext.GetRouteData().Values.TryGetValue("orgId", out var orgIdParam);
|
||||
httpContext.GetRouteData().Values.TryGetValue("orgId", out var orgIdParam);
|
||||
if (orgIdParam == null || !Guid.TryParse(orgIdParam.ToString(), out var orgId))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
|
@ -0,0 +1,36 @@
|
||||
using Bit.Core.AdminConsole.Enums.Provider;
|
||||
using Bit.Core.AdminConsole.Models.Data.Provider;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
|
||||
namespace Bit.Api.AdminConsole.Authorization;
|
||||
|
||||
public static class ProviderOrganizationHttpContextFeature
|
||||
{
|
||||
private static async Task<IEnumerable<ProviderUserOrganizationDetails>> GetProviderUserOrganizationsAsync(
|
||||
this HttpContext httpContext,
|
||||
IProviderUserRepository providerUserRepository,
|
||||
Guid userId)
|
||||
{
|
||||
var providerUserOrganizations = httpContext.Features.Get<IEnumerable<ProviderUserOrganizationDetails>>();
|
||||
if (providerUserOrganizations != null)
|
||||
{
|
||||
return providerUserOrganizations;
|
||||
}
|
||||
|
||||
providerUserOrganizations = (await providerUserRepository.GetManyOrganizationDetailsByUserAsync(
|
||||
userId, ProviderUserStatusType.Confirmed)).ToList();
|
||||
httpContext.Features.Set(providerUserOrganizations);
|
||||
|
||||
return providerUserOrganizations;
|
||||
}
|
||||
|
||||
public static async Task<bool> IsProviderUserForOrgAsync(
|
||||
this HttpContext httpContext,
|
||||
IProviderUserRepository providerUserRepository,
|
||||
Guid userId,
|
||||
Guid organizationId)
|
||||
{
|
||||
var organizations = await httpContext.GetProviderUserOrganizationsAsync(providerUserRepository, userId);
|
||||
return organizations.Any(o => o.OrganizationId == organizationId);
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
#nullable enable
|
||||
|
||||
using Bit.Api.AdminConsole.Context;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
@ -9,11 +8,10 @@ namespace Bit.Api.AdminConsole.Authorization.Requirements;
|
||||
public class ManageUsersRequirement : IOrganizationRequirement
|
||||
{
|
||||
public async Task<bool> AuthorizeAsync(
|
||||
Guid organizationId,
|
||||
CurrentContextOrganization? organizationClaims,
|
||||
IProviderOrganizationContext providerOrganizationContext)
|
||||
Func<Task<bool>> isProviderUserForOrg)
|
||||
=> organizationClaims is
|
||||
{ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or
|
||||
{ Permissions.ManageUsers: true }
|
||||
|| await providerOrganizationContext.ProviderUserForOrgAsync(organizationId);
|
||||
|| await isProviderUserForOrg();
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
#nullable enable
|
||||
|
||||
using Bit.Api.AdminConsole.Context;
|
||||
using Bit.Core.Context;
|
||||
|
||||
namespace Bit.Api.AdminConsole.Authorization.Requirements;
|
||||
@ -11,8 +10,7 @@ namespace Bit.Api.AdminConsole.Authorization.Requirements;
|
||||
public class MemberOrProviderRequirement : IOrganizationRequirement
|
||||
{
|
||||
public async Task<bool> AuthorizeAsync(
|
||||
Guid organizationId,
|
||||
CurrentContextOrganization? organizationClaims,
|
||||
IProviderOrganizationContext providerOrganizationContext)
|
||||
=> organizationClaims is not null || await providerOrganizationContext.ProviderUserForOrgAsync(organizationId);
|
||||
Func<Task<bool>> isProviderUserForOrg)
|
||||
=> organizationClaims is not null || await isProviderUserForOrg();
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System.Security.Claims;
|
||||
using Bit.Api.AdminConsole.Authorization;
|
||||
using Bit.Api.AdminConsole.Context;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -50,7 +49,7 @@ public class OrganizationRequirementHandlerTests
|
||||
// Arrange requirement
|
||||
var testRequirement = Substitute.For<IOrganizationRequirement>();
|
||||
testRequirement
|
||||
.AuthorizeAsync(organizationId, null, Arg.Any<IProviderOrganizationContext>())
|
||||
.AuthorizeAsync(organizationId, null, Arg.Any<Func<Task<bool>>>())
|
||||
.ReturnsForAnyArgs(false);
|
||||
var authContext = new AuthorizationHandlerContext([testRequirement], new ClaimsPrincipal(), null);
|
||||
|
||||
@ -58,7 +57,7 @@ public class OrganizationRequirementHandlerTests
|
||||
await sutProvider.Sut.HandleAsync(authContext);
|
||||
|
||||
// Assert
|
||||
await testRequirement.Received(1).AuthorizeAsync(organizationId, null, Arg.Any<IProviderOrganizationContext>());
|
||||
await testRequirement.Received(1).AuthorizeAsync(organizationId, null, Arg.Any<Func<Task<bool>>>());
|
||||
Assert.False(authContext.HasSucceeded);
|
||||
}
|
||||
|
||||
@ -71,7 +70,7 @@ public class OrganizationRequirementHandlerTests
|
||||
// Arrange requirement
|
||||
var testRequirement = Substitute.For<IOrganizationRequirement>();
|
||||
testRequirement
|
||||
.AuthorizeAsync(organizationId, null, Arg.Any<IProviderOrganizationContext>())
|
||||
.AuthorizeAsync(organizationId, null, Arg.Any<Func<Task<bool>>>())
|
||||
.ReturnsForAnyArgs(true);
|
||||
var authContext = new AuthorizationHandlerContext([testRequirement], new ClaimsPrincipal(), null);
|
||||
|
||||
@ -79,7 +78,7 @@ public class OrganizationRequirementHandlerTests
|
||||
await sutProvider.Sut.HandleAsync(authContext);
|
||||
|
||||
// Assert
|
||||
await testRequirement.Received(1).AuthorizeAsync(organizationId, null, Arg.Any<IProviderOrganizationContext>());
|
||||
await testRequirement.Received(1).AuthorizeAsync(organizationId, null, Arg.Any<Func<Task<bool>>>());
|
||||
Assert.True(authContext.HasSucceeded);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user