1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-26 07:12:20 -05:00

Properly encapsulating this PR behind its feature flag

This commit is contained in:
Conner Turnbull 2025-04-15 14:49:00 -04:00
parent ed430639b5
commit 7c0b7a4bb6
No known key found for this signature in database
4 changed files with 40 additions and 23 deletions

View File

@ -84,6 +84,19 @@ public class OrganizationSponsorshipsController : Controller
throw new BadRequestException("Free Bitwarden Families sponsorship has been disabled by your organization administrator.");
}
if (!_featureService.IsEnabled(Bit.Core.FeatureFlagKeys.PM17772_AdminInitiatedSponsorships))
{
if (model.SponsoringUserId.HasValue)
{
throw new NotFoundException();
}
if (!string.IsNullOrWhiteSpace(model.Notes))
{
model.Notes = null;
}
}
var targetUser = model.SponsoringUserId ?? _currentContext.UserId!.Value;
var sponsorship = await _createSponsorshipCommand.CreateSponsorshipAsync(
sponsoringOrg,

View File

@ -3,6 +3,7 @@ using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -20,6 +21,7 @@ public class SelfHostedOrganizationSponsorshipsController : Controller
private readonly ICreateSponsorshipCommand _offerSponsorshipCommand;
private readonly IRevokeSponsorshipCommand _revokeSponsorshipCommand;
private readonly ICurrentContext _currentContext;
private readonly IFeatureService _featureService;
public SelfHostedOrganizationSponsorshipsController(
ICreateSponsorshipCommand offerSponsorshipCommand,
@ -27,7 +29,8 @@ public class SelfHostedOrganizationSponsorshipsController : Controller
IOrganizationRepository organizationRepository,
IOrganizationSponsorshipRepository organizationSponsorshipRepository,
IOrganizationUserRepository organizationUserRepository,
ICurrentContext currentContext
ICurrentContext currentContext,
IFeatureService featureService
)
{
_offerSponsorshipCommand = offerSponsorshipCommand;
@ -36,14 +39,31 @@ public class SelfHostedOrganizationSponsorshipsController : Controller
_organizationSponsorshipRepository = organizationSponsorshipRepository;
_organizationUserRepository = organizationUserRepository;
_currentContext = currentContext;
_featureService = featureService;
}
[HttpPost("{sponsoringOrgId}/families-for-enterprise")]
public async Task CreateSponsorship(Guid sponsoringOrgId, [FromBody] OrganizationSponsorshipCreateRequestModel model)
{
// Check feature flag at controller level
if (!_featureService.IsEnabled(Bit.Core.FeatureFlagKeys.PM17772_AdminInitiatedSponsorships))
{
// If flag is off and someone tries to use admin-initiated sponsorships, return 404
if (model.SponsoringUserId.HasValue)
{
throw new NotFoundException();
}
// If flag is off and notes field has a value, ignore it
if (!string.IsNullOrWhiteSpace(model.Notes))
{
model.Notes = null;
}
}
await _offerSponsorshipCommand.CreateSponsorshipAsync(
await _organizationRepository.GetByIdAsync(sponsoringOrgId),
await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default),
await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, model.SponsoringUserId ?? _currentContext.UserId ?? default),
model.PlanSponsorshipType, model.SponsoredEmail, model.FriendlyName, model.Notes);
}

View File

@ -13,7 +13,6 @@ namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnte
public class CreateSponsorshipCommand(
ICurrentContext currentContext,
IFeatureService featureService,
IOrganizationSponsorshipRepository organizationSponsorshipRepository,
IUserService userService) : ICreateSponsorshipCommand
{
@ -65,11 +64,6 @@ public class CreateSponsorshipCommand(
var isAdminInitiated = false;
if (currentContext.UserId != sponsoringMember.UserId)
{
if (!featureService.IsEnabled(FeatureFlagKeys.PM17772_AdminInitiatedSponsorships))
{
throw new BadRequestException("Feature 'pm-17772-admin-initiated-sponsorships' is not enabled.");
}
var organization = currentContext.Organizations.First(x => x.Id == sponsoringOrganization.Id);
OrganizationUserType[] allowedUserTypes =
[

View File

@ -225,10 +225,6 @@ public class CreateSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
sponsoringOrg.PlanType = PlanType.EnterpriseAnnually;
sponsoringOrgUser.Status = OrganizationUserStatusType.Confirmed;
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(Arg.Is<string>(p => p == FeatureFlagKeys.PM17772_AdminInitiatedSponsorships))
.Returns(true);
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(sponsoringOrgUser.UserId!.Value).Returns(user);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>().WhenForAnyArgs(x => x.UpsertAsync(null!)).Do(callInfo =>
{
@ -248,7 +244,7 @@ public class CreateSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
var actual = await Assert.ThrowsAsync<UnauthorizedAccessException>(async () =>
await sutProvider.Sut.CreateSponsorshipAsync(sponsoringOrg, sponsoringOrgUser,
PlanSponsorshipType.FamiliesForEnterprise, sponsoredEmail, friendlyName, null));
PlanSponsorshipType.FamiliesForEnterprise, sponsoredEmail, friendlyName, notes: null));
Assert.Equal("You do not have permissions to send sponsorships on behalf of the organization.", actual.Message);
}
@ -264,10 +260,6 @@ public class CreateSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
sponsoringOrg.PlanType = PlanType.EnterpriseAnnually;
sponsoringOrgUser.Status = OrganizationUserStatusType.Confirmed;
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(Arg.Is<string>(p => p == FeatureFlagKeys.PM17772_AdminInitiatedSponsorships))
.Returns(true);
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(sponsoringOrgUser.UserId!.Value).Returns(user);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>().WhenForAnyArgs(x => x.UpsertAsync(null!)).Do(callInfo =>
{
@ -279,14 +271,14 @@ public class CreateSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
new()
{
Id = sponsoringOrg.Id,
Permissions = new Permissions(),
Type = organizationUserType
}
]);
var actual = await Assert.ThrowsAsync<UnauthorizedAccessException>(async () =>
await sutProvider.Sut.CreateSponsorshipAsync(sponsoringOrg, sponsoringOrgUser,
PlanSponsorshipType.FamiliesForEnterprise, sponsoredEmail, friendlyName, null));
PlanSponsorshipType.FamiliesForEnterprise, sponsoredEmail, friendlyName, notes: null));
Assert.Equal("You do not have permissions to send sponsorships on behalf of the organization.", actual.Message);
}
@ -300,12 +292,9 @@ public class CreateSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
string friendlyName, Guid sponsorshipId, Guid currentUserId, string notes, SutProvider<CreateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = PlanType.EnterpriseAnnually;
sponsoringOrg.UseAdminSponsoredFamilies = true;
sponsoringOrgUser.Status = OrganizationUserStatusType.Confirmed;
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(Arg.Is<string>(p => p == FeatureFlagKeys.PM17772_AdminInitiatedSponsorships))
.Returns(true);
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(sponsoringOrgUser.UserId!.Value).Returns(user);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>().WhenForAnyArgs(x => x.UpsertAsync(null!)).Do(callInfo =>
{
@ -317,6 +306,7 @@ public class CreateSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
new()
{
Id = sponsoringOrg.Id,
Permissions = new Permissions { ManageUsers = true },
Type = organizationUserType
}
]);