mirror of
https://github.com/bitwarden/server.git
synced 2025-04-14 01:28:14 -05:00
WIP
This commit is contained in:
parent
989f64d449
commit
c03190d672
src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise
@ -1,95 +1,48 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.Billing.Extensions;
|
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SponsorshipCreation;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
|
|
||||||
namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
|
namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
|
||||||
|
|
||||||
public class CreateSponsorshipCommand(
|
public class CreateSponsorshipCommand : ICreateSponsorshipCommand
|
||||||
IOrganizationSponsorshipRepository organizationSponsorshipRepository,
|
|
||||||
IUserService userService,
|
|
||||||
ICurrentContext currentContext)
|
|
||||||
: ICreateSponsorshipCommand
|
|
||||||
{
|
{
|
||||||
|
private readonly IOrganizationSponsorshipRepository _organizationSponsorshipRepository;
|
||||||
|
|
||||||
|
private readonly BaseCreateSponsorshipHandler _createSponsorshipHandler;
|
||||||
|
|
||||||
|
public CreateSponsorshipCommand(
|
||||||
|
IOrganizationSponsorshipRepository organizationSponsorshipRepository,
|
||||||
|
IUserService userService,
|
||||||
|
ICurrentContext currentContext)
|
||||||
|
{
|
||||||
|
_organizationSponsorshipRepository = organizationSponsorshipRepository;
|
||||||
|
|
||||||
|
var adminInitiatedSponsorshipHandler = new CreateAdminInitiatedSponsorshipHandler(currentContext);
|
||||||
|
_createSponsorshipHandler = new CreateSponsorshipHandler(userService, organizationSponsorshipRepository);
|
||||||
|
_createSponsorshipHandler.SetNext(adminInitiatedSponsorshipHandler);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<OrganizationSponsorship> CreateSponsorshipAsync(Organization sponsoringOrg, OrganizationUser sponsoringOrgUser,
|
public async Task<OrganizationSponsorship> CreateSponsorshipAsync(Organization sponsoringOrg, OrganizationUser sponsoringOrgUser,
|
||||||
PlanSponsorshipType sponsorshipType, string sponsoredEmail, string friendlyName)
|
PlanSponsorshipType sponsorshipType, string sponsoredEmail, string friendlyName)
|
||||||
{
|
{
|
||||||
var sponsoringUser = await userService.GetUserByIdAsync(sponsoringOrgUser.UserId.Value);
|
var createSponsorshipRequest = new CreateSponsorshipRequest(sponsoringOrg, sponsoringOrgUser, sponsorshipType, sponsoredEmail, friendlyName);
|
||||||
if (sponsoringUser == null || string.Equals(sponsoringUser.Email, sponsoredEmail, System.StringComparison.InvariantCultureIgnoreCase))
|
var sponsorship = await _createSponsorshipHandler.HandleAsync(createSponsorshipRequest);
|
||||||
{
|
|
||||||
throw new BadRequestException("Cannot offer a Families Organization Sponsorship to yourself. Choose a different email.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var requiredSponsoringProductType = StaticStore.GetSponsoredPlan(sponsorshipType)?.SponsoringProductTierType;
|
|
||||||
var sponsoringOrgProductTier = sponsoringOrg.PlanType.GetProductTier();
|
|
||||||
|
|
||||||
if (requiredSponsoringProductType == null ||
|
|
||||||
sponsoringOrgProductTier != requiredSponsoringProductType.Value)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Specified Organization cannot sponsor other organizations.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sponsoringOrgUser == null || sponsoringOrgUser.Status != OrganizationUserStatusType.Confirmed)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Only confirmed users can sponsor other organizations.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var isAdminInitiated = false;
|
|
||||||
if (currentContext.UserId != sponsoringOrgUser.UserId)
|
|
||||||
{
|
|
||||||
var organization = currentContext.Organizations.First(x => x.Id == sponsoringOrg.Id);
|
|
||||||
OrganizationUserType[] allowedUserTypes =
|
|
||||||
[
|
|
||||||
OrganizationUserType.Admin,
|
|
||||||
OrganizationUserType.Owner,
|
|
||||||
OrganizationUserType.Custom
|
|
||||||
];
|
|
||||||
if (!organization.Permissions.ManageUsers || allowedUserTypes.All(x => x != organization.Type))
|
|
||||||
{
|
|
||||||
throw new UnauthorizedAccessException("You do not have permissions to send sponsorships on behalf of the organization.");
|
|
||||||
}
|
|
||||||
isAdminInitiated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var existingOrgSponsorship = await organizationSponsorshipRepository
|
|
||||||
.GetBySponsoringOrganizationUserIdAsync(sponsoringOrgUser.Id);
|
|
||||||
if (existingOrgSponsorship?.SponsoredOrganizationId != null)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Can only sponsor one organization per Organization User.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var sponsorship = new OrganizationSponsorship
|
|
||||||
{
|
|
||||||
SponsoringOrganizationId = sponsoringOrg.Id,
|
|
||||||
SponsoringOrganizationUserId = sponsoringOrgUser.Id,
|
|
||||||
FriendlyName = friendlyName,
|
|
||||||
OfferedToEmail = sponsoredEmail,
|
|
||||||
PlanSponsorshipType = sponsorshipType,
|
|
||||||
IsAdminInitiated = isAdminInitiated
|
|
||||||
};
|
|
||||||
|
|
||||||
if (existingOrgSponsorship != null)
|
|
||||||
{
|
|
||||||
// Replace existing invalid offer with our new sponsorship offer
|
|
||||||
sponsorship.Id = existingOrgSponsorship.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await organizationSponsorshipRepository.UpsertAsync(sponsorship);
|
await _organizationSponsorshipRepository.UpsertAsync(sponsorship);
|
||||||
return sponsorship;
|
return sponsorship;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
if (sponsorship.Id != default)
|
if (sponsorship.Id != Guid.Empty)
|
||||||
{
|
{
|
||||||
await organizationSponsorshipRepository.DeleteAsync(sponsorship);
|
await _organizationSponsorshipRepository.DeleteAsync(sponsorship);
|
||||||
}
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
23
src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/SponsorshipCreation/BaseCreateSponsorshipHandler.cs
Normal file
23
src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/SponsorshipCreation/BaseCreateSponsorshipHandler.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
|
||||||
|
namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SponsorshipCreation;
|
||||||
|
|
||||||
|
public abstract class BaseCreateSponsorshipHandler
|
||||||
|
{
|
||||||
|
private BaseCreateSponsorshipHandler _next;
|
||||||
|
|
||||||
|
public BaseCreateSponsorshipHandler SetNext(BaseCreateSponsorshipHandler next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task<OrganizationSponsorship> HandleAsync(CreateSponsorshipRequest request)
|
||||||
|
{
|
||||||
|
if (_next != null)
|
||||||
|
{
|
||||||
|
return await _next.HandleAsync(request);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
|
||||||
|
namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SponsorshipCreation;
|
||||||
|
|
||||||
|
public class CreateAdminInitiatedSponsorshipHandler(
|
||||||
|
ICurrentContext currentContext) : BaseCreateSponsorshipHandler
|
||||||
|
{
|
||||||
|
public override async Task<OrganizationSponsorship> HandleAsync(CreateSponsorshipRequest request)
|
||||||
|
{
|
||||||
|
var isAdminInitiated = false;
|
||||||
|
if (currentContext.UserId != request.SponsoringMember.UserId)
|
||||||
|
{
|
||||||
|
var organization = currentContext.Organizations.First(x => x.Id == request.SponsoringOrganization.Id);
|
||||||
|
OrganizationUserType[] allowedUserTypes =
|
||||||
|
[
|
||||||
|
OrganizationUserType.Admin,
|
||||||
|
OrganizationUserType.Owner,
|
||||||
|
OrganizationUserType.Custom
|
||||||
|
];
|
||||||
|
if (!organization.Permissions.ManageUsers || allowedUserTypes.All(x => x != organization.Type))
|
||||||
|
{
|
||||||
|
throw new UnauthorizedAccessException("You do not have permissions to send sponsorships on behalf of the organization.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!request.SponsoringOrganization.UseAdminSponsoredFamilies)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Sponsoring organization cannot sponsor other Family organizations.");
|
||||||
|
}
|
||||||
|
|
||||||
|
isAdminInitiated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sponsorship = await base.HandleAsync(request) ?? new OrganizationSponsorship();
|
||||||
|
|
||||||
|
sponsorship.IsAdminInitiated = isAdminInitiated;
|
||||||
|
|
||||||
|
return sponsorship;
|
||||||
|
}
|
||||||
|
}
|
61
src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/SponsorshipCreation/CreateSponsorshipHandler.cs
Normal file
61
src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/SponsorshipCreation/CreateSponsorshipHandler.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
using Bit.Core.Billing.Extensions;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
|
namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SponsorshipCreation;
|
||||||
|
|
||||||
|
public class CreateSponsorshipHandler(
|
||||||
|
IUserService userService,
|
||||||
|
IOrganizationSponsorshipRepository organizationSponsorshipRepository) : BaseCreateSponsorshipHandler
|
||||||
|
{
|
||||||
|
public override async Task<OrganizationSponsorship> HandleAsync(CreateSponsorshipRequest request)
|
||||||
|
{
|
||||||
|
var sponsoringUser = await userService.GetUserByIdAsync(request.SponsoringMember.UserId.Value);
|
||||||
|
|
||||||
|
if (sponsoringUser == null || string.Equals(sponsoringUser.Email, request.SponsoredEmail, System.StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Cannot offer a Families Organization Sponsorship to yourself. Choose a different email.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var requiredSponsoringProductType = StaticStore.GetSponsoredPlan(request.SponsorshipType)?.SponsoringProductTierType;
|
||||||
|
var sponsoringOrgProductTier = request.SponsoringOrganization.PlanType.GetProductTier();
|
||||||
|
|
||||||
|
if (requiredSponsoringProductType == null ||
|
||||||
|
sponsoringOrgProductTier != requiredSponsoringProductType.Value)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Specified Organization cannot sponsor other organizations.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.SponsoringMember == null || request.SponsoringMember.Status != OrganizationUserStatusType.Confirmed)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Only confirmed users can sponsor other organizations.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingOrgSponsorship = await organizationSponsorshipRepository
|
||||||
|
.GetBySponsoringOrganizationUserIdAsync(request.SponsoringMember.Id);
|
||||||
|
if (existingOrgSponsorship?.SponsoredOrganizationId != null)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Can only sponsor one organization per Organization User.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var sponsorship = await base.HandleAsync(request) ?? new OrganizationSponsorship();
|
||||||
|
|
||||||
|
sponsorship.SponsoringOrganizationId = request.SponsoringOrganization.Id;
|
||||||
|
sponsorship.SponsoringOrganizationUserId = request.SponsoringMember.Id;
|
||||||
|
sponsorship.FriendlyName = request.FriendlyName;
|
||||||
|
sponsorship.OfferedToEmail = request.SponsoredEmail;
|
||||||
|
sponsorship.PlanSponsorshipType = request.SponsorshipType;
|
||||||
|
|
||||||
|
if (existingOrgSponsorship != null)
|
||||||
|
{
|
||||||
|
// Replace existing invalid offer with our new sponsorship offer
|
||||||
|
sponsorship.Id = existingOrgSponsorship.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sponsorship;
|
||||||
|
}
|
||||||
|
}
|
12
src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/SponsorshipCreation/CreateSponsorshipRequest.cs
Normal file
12
src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/SponsorshipCreation/CreateSponsorshipRequest.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SponsorshipCreation;
|
||||||
|
|
||||||
|
public record CreateSponsorshipRequest(
|
||||||
|
Organization SponsoringOrganization,
|
||||||
|
OrganizationUser SponsoringMember,
|
||||||
|
PlanSponsorshipType SponsorshipType,
|
||||||
|
string SponsoredEmail,
|
||||||
|
string FriendlyName);
|
Loading…
x
Reference in New Issue
Block a user