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
|
#nullable enable
|
||||||
|
|
||||||
using Bit.Api.AdminConsole.Context;
|
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
|
||||||
@ -15,7 +14,6 @@ namespace Bit.Api.AdminConsole.Authorization;
|
|||||||
public interface IOrganizationRequirement : IAuthorizationRequirement
|
public interface IOrganizationRequirement : IAuthorizationRequirement
|
||||||
{
|
{
|
||||||
public Task<bool> AuthorizeAsync(
|
public Task<bool> AuthorizeAsync(
|
||||||
Guid organizationId,
|
|
||||||
CurrentContextOrganization? organizationClaims,
|
CurrentContextOrganization? organizationClaims,
|
||||||
IProviderOrganizationContext providerOrganizationContext);
|
Func<Task<bool>> isProviderUserForOrg);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ using Bit.Core.Models.Data;
|
|||||||
|
|
||||||
namespace Bit.Api.AdminConsole.Authorization;
|
namespace Bit.Api.AdminConsole.Authorization;
|
||||||
|
|
||||||
public static class ClaimsExtensions
|
public static class OrganizationClaimsExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A delegate that returns true if the user has the specified claim type for an organization, false otherwise.
|
/// A delegate that returns true if the user has the specified claim type for an organization, false otherwise.
|
@ -1,6 +1,9 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using BadRequestException = Bit.Core.Exceptions.BadRequestException;
|
||||||
|
|
||||||
namespace Bit.Api.AdminConsole.Authorization;
|
namespace Bit.Api.AdminConsole.Authorization;
|
||||||
|
|
||||||
@ -10,17 +13,31 @@ namespace Bit.Api.AdminConsole.Authorization;
|
|||||||
/// determine whether the action is authorized.
|
/// determine whether the action is authorized.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class OrganizationRequirementHandler(
|
public class OrganizationRequirementHandler(
|
||||||
IHttpContextAccessor httpContextAccessor)
|
IHttpContextAccessor httpContextAccessor,
|
||||||
|
IProviderUserRepository providerUserRepository,
|
||||||
|
IUserService userService)
|
||||||
: AuthorizationHandler<IOrganizationRequirement>
|
: AuthorizationHandler<IOrganizationRequirement>
|
||||||
{
|
{
|
||||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, IOrganizationRequirement requirement)
|
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 organizationId = httpContext.GetOrganizationId();
|
||||||
var providerOrganizationContext = null; // TODO
|
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)
|
if (authorized)
|
||||||
{
|
{
|
||||||
|
@ -4,14 +4,9 @@ namespace Bit.Api.AdminConsole.Authorization;
|
|||||||
|
|
||||||
public static class OrganizationRequirementHelpers
|
public static class OrganizationRequirementHelpers
|
||||||
{
|
{
|
||||||
public static Guid GetOrganizationId(this IHttpContextAccessor httpContextAccessor)
|
public static Guid GetOrganizationId(this HttpContext httpContext)
|
||||||
{
|
{
|
||||||
if (httpContextAccessor.HttpContext is null)
|
httpContext.GetRouteData().Values.TryGetValue("orgId", out var orgIdParam);
|
||||||
{
|
|
||||||
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);
|
|
||||||
if (orgIdParam == null || !Guid.TryParse(orgIdParam.ToString(), out var orgId))
|
if (orgIdParam == null || !Guid.TryParse(orgIdParam.ToString(), out var orgId))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(
|
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
|
#nullable enable
|
||||||
|
|
||||||
using Bit.Api.AdminConsole.Context;
|
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
@ -9,11 +8,10 @@ namespace Bit.Api.AdminConsole.Authorization.Requirements;
|
|||||||
public class ManageUsersRequirement : IOrganizationRequirement
|
public class ManageUsersRequirement : IOrganizationRequirement
|
||||||
{
|
{
|
||||||
public async Task<bool> AuthorizeAsync(
|
public async Task<bool> AuthorizeAsync(
|
||||||
Guid organizationId,
|
|
||||||
CurrentContextOrganization? organizationClaims,
|
CurrentContextOrganization? organizationClaims,
|
||||||
IProviderOrganizationContext providerOrganizationContext)
|
Func<Task<bool>> isProviderUserForOrg)
|
||||||
=> organizationClaims is
|
=> organizationClaims is
|
||||||
{ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or
|
{ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or
|
||||||
{ Permissions.ManageUsers: true }
|
{ Permissions.ManageUsers: true }
|
||||||
|| await providerOrganizationContext.ProviderUserForOrgAsync(organizationId);
|
|| await isProviderUserForOrg();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
using Bit.Api.AdminConsole.Context;
|
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
|
|
||||||
namespace Bit.Api.AdminConsole.Authorization.Requirements;
|
namespace Bit.Api.AdminConsole.Authorization.Requirements;
|
||||||
@ -11,8 +10,7 @@ namespace Bit.Api.AdminConsole.Authorization.Requirements;
|
|||||||
public class MemberOrProviderRequirement : IOrganizationRequirement
|
public class MemberOrProviderRequirement : IOrganizationRequirement
|
||||||
{
|
{
|
||||||
public async Task<bool> AuthorizeAsync(
|
public async Task<bool> AuthorizeAsync(
|
||||||
Guid organizationId,
|
|
||||||
CurrentContextOrganization? organizationClaims,
|
CurrentContextOrganization? organizationClaims,
|
||||||
IProviderOrganizationContext providerOrganizationContext)
|
Func<Task<bool>> isProviderUserForOrg)
|
||||||
=> organizationClaims is not null || await providerOrganizationContext.ProviderUserForOrgAsync(organizationId);
|
=> organizationClaims is not null || await isProviderUserForOrg();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Bit.Api.AdminConsole.Authorization;
|
using Bit.Api.AdminConsole.Authorization;
|
||||||
using Bit.Api.AdminConsole.Context;
|
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
@ -50,7 +49,7 @@ public class OrganizationRequirementHandlerTests
|
|||||||
// Arrange requirement
|
// Arrange requirement
|
||||||
var testRequirement = Substitute.For<IOrganizationRequirement>();
|
var testRequirement = Substitute.For<IOrganizationRequirement>();
|
||||||
testRequirement
|
testRequirement
|
||||||
.AuthorizeAsync(organizationId, null, Arg.Any<IProviderOrganizationContext>())
|
.AuthorizeAsync(organizationId, null, Arg.Any<Func<Task<bool>>>())
|
||||||
.ReturnsForAnyArgs(false);
|
.ReturnsForAnyArgs(false);
|
||||||
var authContext = new AuthorizationHandlerContext([testRequirement], new ClaimsPrincipal(), null);
|
var authContext = new AuthorizationHandlerContext([testRequirement], new ClaimsPrincipal(), null);
|
||||||
|
|
||||||
@ -58,7 +57,7 @@ public class OrganizationRequirementHandlerTests
|
|||||||
await sutProvider.Sut.HandleAsync(authContext);
|
await sutProvider.Sut.HandleAsync(authContext);
|
||||||
|
|
||||||
// Assert
|
// 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);
|
Assert.False(authContext.HasSucceeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +70,7 @@ public class OrganizationRequirementHandlerTests
|
|||||||
// Arrange requirement
|
// Arrange requirement
|
||||||
var testRequirement = Substitute.For<IOrganizationRequirement>();
|
var testRequirement = Substitute.For<IOrganizationRequirement>();
|
||||||
testRequirement
|
testRequirement
|
||||||
.AuthorizeAsync(organizationId, null, Arg.Any<IProviderOrganizationContext>())
|
.AuthorizeAsync(organizationId, null, Arg.Any<Func<Task<bool>>>())
|
||||||
.ReturnsForAnyArgs(true);
|
.ReturnsForAnyArgs(true);
|
||||||
var authContext = new AuthorizationHandlerContext([testRequirement], new ClaimsPrincipal(), null);
|
var authContext = new AuthorizationHandlerContext([testRequirement], new ClaimsPrincipal(), null);
|
||||||
|
|
||||||
@ -79,7 +78,7 @@ public class OrganizationRequirementHandlerTests
|
|||||||
await sutProvider.Sut.HandleAsync(authContext);
|
await sutProvider.Sut.HandleAsync(authContext);
|
||||||
|
|
||||||
// Assert
|
// 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);
|
Assert.True(authContext.HasSucceeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user