mirror of
https://github.com/bitwarden/server.git
synced 2025-05-23 04:21:05 -05:00
WIP
This commit is contained in:
parent
224ef1272e
commit
08924e10d2
@ -77,9 +77,10 @@ public class OrganizationSponsorshipsController : Controller
|
|||||||
{
|
{
|
||||||
var sponsoringOrg = await _organizationRepository.GetByIdAsync(sponsoringOrgId);
|
var sponsoringOrg = await _organizationRepository.GetByIdAsync(sponsoringOrgId);
|
||||||
|
|
||||||
|
var targetUser = model.SponsoringUserId ?? _currentContext.UserId!.Value;
|
||||||
var sponsorship = await _createSponsorshipCommand.CreateSponsorshipAsync(
|
var sponsorship = await _createSponsorshipCommand.CreateSponsorshipAsync(
|
||||||
sponsoringOrg,
|
sponsoringOrg,
|
||||||
await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default),
|
await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, targetUser),
|
||||||
model.PlanSponsorshipType, model.SponsoredEmail, model.FriendlyName);
|
model.PlanSponsorshipType, model.SponsoredEmail, model.FriendlyName);
|
||||||
await _sendSponsorshipOfferCommand.SendSponsorshipOfferAsync(sponsorship, sponsoringOrg.Name);
|
await _sendSponsorshipOfferCommand.SendSponsorshipOfferAsync(sponsorship, sponsoringOrg.Name);
|
||||||
}
|
}
|
||||||
|
@ -16,4 +16,10 @@ public class OrganizationSponsorshipCreateRequestModel
|
|||||||
|
|
||||||
[StringLength(256)]
|
[StringLength(256)]
|
||||||
public string FriendlyName { get; set; }
|
public string FriendlyName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// (optional) The user to target for the sponsorship.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Left empty when creating a sponsorship for the authenticated user.</remarks>
|
||||||
|
public Guid? SponsoringUserId { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -114,6 +114,11 @@ public class Organization : ITableObject<Guid>, IStorableSubscriber, IRevisable,
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UseRiskInsights { get; set; }
|
public bool UseRiskInsights { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If set to true, admins can initiate organization-issued sponsorships.
|
||||||
|
/// </summary>
|
||||||
|
public bool UseAdminSponsoredFamilies { get; set; }
|
||||||
|
|
||||||
public void SetNewId()
|
public void SetNewId()
|
||||||
{
|
{
|
||||||
if (Id == default(Guid))
|
if (Id == default(Guid))
|
||||||
|
@ -26,6 +26,7 @@ public class OrganizationAbility
|
|||||||
LimitItemDeletion = organization.LimitItemDeletion;
|
LimitItemDeletion = organization.LimitItemDeletion;
|
||||||
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
|
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
|
||||||
UseRiskInsights = organization.UseRiskInsights;
|
UseRiskInsights = organization.UseRiskInsights;
|
||||||
|
UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
@ -45,4 +46,5 @@ public class OrganizationAbility
|
|||||||
public bool LimitItemDeletion { get; set; }
|
public bool LimitItemDeletion { get; set; }
|
||||||
public bool AllowAdminAccessToAllCollectionItems { get; set; }
|
public bool AllowAdminAccessToAllCollectionItems { get; set; }
|
||||||
public bool UseRiskInsights { get; set; }
|
public bool UseRiskInsights { get; set; }
|
||||||
|
public bool UseAdminSponsoredFamilies { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ public class OrganizationSponsorship : ITableObject<Guid>
|
|||||||
public DateTime? LastSyncDate { get; set; }
|
public DateTime? LastSyncDate { get; set; }
|
||||||
public DateTime? ValidUntil { get; set; }
|
public DateTime? ValidUntil { get; set; }
|
||||||
public bool ToDelete { get; set; }
|
public bool ToDelete { get; set; }
|
||||||
|
public bool IsAdminInitiated { get; set; }
|
||||||
|
|
||||||
public void SetNewId()
|
public void SetNewId()
|
||||||
{
|
{
|
||||||
|
@ -16,6 +16,7 @@ public class OrganizationSponsorshipData
|
|||||||
LastSyncDate = sponsorship.LastSyncDate;
|
LastSyncDate = sponsorship.LastSyncDate;
|
||||||
ValidUntil = sponsorship.ValidUntil;
|
ValidUntil = sponsorship.ValidUntil;
|
||||||
ToDelete = sponsorship.ToDelete;
|
ToDelete = sponsorship.ToDelete;
|
||||||
|
IsAdminInitiated = sponsorship.IsAdminInitiated;
|
||||||
}
|
}
|
||||||
public Guid SponsoringOrganizationUserId { get; set; }
|
public Guid SponsoringOrganizationUserId { get; set; }
|
||||||
public Guid? SponsoredOrganizationId { get; set; }
|
public Guid? SponsoredOrganizationId { get; set; }
|
||||||
@ -25,6 +26,7 @@ public class OrganizationSponsorshipData
|
|||||||
public DateTime? LastSyncDate { get; set; }
|
public DateTime? LastSyncDate { get; set; }
|
||||||
public DateTime? ValidUntil { get; set; }
|
public DateTime? ValidUntil { get; set; }
|
||||||
public bool ToDelete { get; set; }
|
public bool ToDelete { get; set; }
|
||||||
|
public bool IsAdminInitiated { get; set; }
|
||||||
|
|
||||||
public bool CloudSponsorshipRemoved { get; set; }
|
public bool CloudSponsorshipRemoved { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.Billing.Extensions;
|
using Bit.Core.Billing.Extensions;
|
||||||
|
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.Exceptions;
|
||||||
@ -10,22 +11,16 @@ using Bit.Core.Utilities;
|
|||||||
|
|
||||||
namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
|
namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
|
||||||
|
|
||||||
public class CreateSponsorshipCommand : ICreateSponsorshipCommand
|
public class CreateSponsorshipCommand(
|
||||||
|
IOrganizationSponsorshipRepository organizationSponsorshipRepository,
|
||||||
|
IUserService userService,
|
||||||
|
ICurrentContext currentContext)
|
||||||
|
: ICreateSponsorshipCommand
|
||||||
{
|
{
|
||||||
private readonly IOrganizationSponsorshipRepository _organizationSponsorshipRepository;
|
|
||||||
private readonly IUserService _userService;
|
|
||||||
|
|
||||||
public CreateSponsorshipCommand(IOrganizationSponsorshipRepository organizationSponsorshipRepository,
|
|
||||||
IUserService userService)
|
|
||||||
{
|
|
||||||
_organizationSponsorshipRepository = organizationSponsorshipRepository;
|
|
||||||
_userService = userService;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 sponsoringUser = await userService.GetUserByIdAsync(sponsoringOrgUser.UserId.Value);
|
||||||
if (sponsoringUser == null || string.Equals(sponsoringUser.Email, sponsoredEmail, System.StringComparison.InvariantCultureIgnoreCase))
|
if (sponsoringUser == null || string.Equals(sponsoringUser.Email, sponsoredEmail, System.StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Cannot offer a Families Organization Sponsorship to yourself. Choose a different email.");
|
throw new BadRequestException("Cannot offer a Families Organization Sponsorship to yourself. Choose a different email.");
|
||||||
@ -45,7 +40,24 @@ public class CreateSponsorshipCommand : ICreateSponsorshipCommand
|
|||||||
throw new BadRequestException("Only confirmed users can sponsor other organizations.");
|
throw new BadRequestException("Only confirmed users can sponsor other organizations.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var existingOrgSponsorship = await _organizationSponsorshipRepository
|
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);
|
.GetBySponsoringOrganizationUserIdAsync(sponsoringOrgUser.Id);
|
||||||
if (existingOrgSponsorship?.SponsoredOrganizationId != null)
|
if (existingOrgSponsorship?.SponsoredOrganizationId != null)
|
||||||
{
|
{
|
||||||
@ -59,6 +71,7 @@ public class CreateSponsorshipCommand : ICreateSponsorshipCommand
|
|||||||
FriendlyName = friendlyName,
|
FriendlyName = friendlyName,
|
||||||
OfferedToEmail = sponsoredEmail,
|
OfferedToEmail = sponsoredEmail,
|
||||||
PlanSponsorshipType = sponsorshipType,
|
PlanSponsorshipType = sponsorshipType,
|
||||||
|
IsAdminInitiated = isAdminInitiated
|
||||||
};
|
};
|
||||||
|
|
||||||
if (existingOrgSponsorship != null)
|
if (existingOrgSponsorship != null)
|
||||||
@ -69,14 +82,14 @@ public class CreateSponsorshipCommand : ICreateSponsorshipCommand
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _organizationSponsorshipRepository.UpsertAsync(sponsorship);
|
await organizationSponsorshipRepository.UpsertAsync(sponsorship);
|
||||||
return sponsorship;
|
return sponsorship;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
if (sponsorship.Id != default)
|
if (sponsorship.Id != default)
|
||||||
{
|
{
|
||||||
await _organizationSponsorshipRepository.DeleteAsync(sponsorship);
|
await organizationSponsorshipRepository.DeleteAsync(sponsorship);
|
||||||
}
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,7 @@ CREATE TABLE [dbo].[Organization] (
|
|||||||
[LimitItemDeletion] BIT NOT NULL CONSTRAINT [DF_Organization_LimitItemDeletion] DEFAULT (0),
|
[LimitItemDeletion] BIT NOT NULL CONSTRAINT [DF_Organization_LimitItemDeletion] DEFAULT (0),
|
||||||
[AllowAdminAccessToAllCollectionItems] BIT NOT NULL CONSTRAINT [DF_Organization_AllowAdminAccessToAllCollectionItems] DEFAULT (0),
|
[AllowAdminAccessToAllCollectionItems] BIT NOT NULL CONSTRAINT [DF_Organization_AllowAdminAccessToAllCollectionItems] DEFAULT (0),
|
||||||
[UseRiskInsights] BIT NOT NULL CONSTRAINT [DF_Organization_UseRiskInsights] DEFAULT (0),
|
[UseRiskInsights] BIT NOT NULL CONSTRAINT [DF_Organization_UseRiskInsights] DEFAULT (0),
|
||||||
|
[UseAdminSponsoredFamilies] BIT NOT NULL CONSTRAINT [DF_Organization_UseAdminSponsoredFamilies] DEFAULT (0),
|
||||||
CONSTRAINT [PK_Organization] PRIMARY KEY CLUSTERED ([Id] ASC)
|
CONSTRAINT [PK_Organization] PRIMARY KEY CLUSTERED ([Id] ASC)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ CREATE TABLE [dbo].[OrganizationSponsorship] (
|
|||||||
[ToDelete] BIT DEFAULT (0) NOT NULL,
|
[ToDelete] BIT DEFAULT (0) NOT NULL,
|
||||||
[LastSyncDate] DATETIME2 (7) NULL,
|
[LastSyncDate] DATETIME2 (7) NULL,
|
||||||
[ValidUntil] DATETIME2 (7) NULL,
|
[ValidUntil] DATETIME2 (7) NULL,
|
||||||
|
[IsAdminInitiated] BIT NOT NULL CONSTRAINT [DF_OrganizationSponsorship_IsAdminInitiated] DEFAULT (0),
|
||||||
CONSTRAINT [PK_OrganizationSponsorship] PRIMARY KEY CLUSTERED ([Id] ASC),
|
CONSTRAINT [PK_OrganizationSponsorship] PRIMARY KEY CLUSTERED ([Id] ASC),
|
||||||
CONSTRAINT [FK_OrganizationSponsorship_SponsoringOrg] FOREIGN KEY ([SponsoringOrganizationId]) REFERENCES [dbo].[Organization] ([Id]),
|
CONSTRAINT [FK_OrganizationSponsorship_SponsoringOrg] FOREIGN KEY ([SponsoringOrganizationId]) REFERENCES [dbo].[Organization] ([Id]),
|
||||||
CONSTRAINT [FK_OrganizationSponsorship_SponsoredOrg] FOREIGN KEY ([SponsoredOrganizationId]) REFERENCES [dbo].[Organization] ([Id]),
|
CONSTRAINT [FK_OrganizationSponsorship_SponsoredOrg] FOREIGN KEY ([SponsoredOrganizationId]) REFERENCES [dbo].[Organization] ([Id]),
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
|
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.Exceptions;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
|
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
@ -110,6 +112,8 @@ public class CreateSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
|
|||||||
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||||
.GetBySponsoringOrganizationUserIdAsync(orgUser.Id).Returns(sponsorship);
|
.GetBySponsoringOrganizationUserIdAsync(orgUser.Id).Returns(sponsorship);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(orgUser.UserId.Value);
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||||
sutProvider.Sut.CreateSponsorshipAsync(org, orgUser, sponsorship.PlanSponsorshipType.Value, default, default));
|
sutProvider.Sut.CreateSponsorshipAsync(org, orgUser, sponsorship.PlanSponsorshipType.Value, default, default));
|
||||||
|
|
||||||
@ -132,6 +136,7 @@ public class CreateSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
|
|||||||
var sponsorship = callInfo.Arg<OrganizationSponsorship>();
|
var sponsorship = callInfo.Arg<OrganizationSponsorship>();
|
||||||
sponsorship.Id = sponsorshipId;
|
sponsorship.Id = sponsorshipId;
|
||||||
});
|
});
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(sponsoringOrgUser.UserId.Value);
|
||||||
|
|
||||||
|
|
||||||
await sutProvider.Sut.CreateSponsorshipAsync(sponsoringOrg, sponsoringOrgUser,
|
await sutProvider.Sut.CreateSponsorshipAsync(sponsoringOrg, sponsoringOrgUser,
|
||||||
@ -168,6 +173,7 @@ public class CreateSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
|
|||||||
createdSponsorship.Id = Guid.NewGuid();
|
createdSponsorship.Id = Guid.NewGuid();
|
||||||
return expectedException;
|
return expectedException;
|
||||||
});
|
});
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(sponsoringOrgUser.UserId.Value);
|
||||||
|
|
||||||
var actualException = await Assert.ThrowsAsync<Exception>(() =>
|
var actualException = await Assert.ThrowsAsync<Exception>(() =>
|
||||||
sutProvider.Sut.CreateSponsorshipAsync(sponsoringOrg, sponsoringOrgUser,
|
sutProvider.Sut.CreateSponsorshipAsync(sponsoringOrg, sponsoringOrgUser,
|
||||||
@ -177,4 +183,74 @@ public class CreateSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
|
|||||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1)
|
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1)
|
||||||
.DeleteAsync(createdSponsorship);
|
.DeleteAsync(createdSponsorship);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task CreateSponsorship_MissingManageUsersPermission_ThrowsUnauthorizedException(
|
||||||
|
Organization sponsoringOrg, OrganizationUser sponsoringOrgUser, User user, string sponsoredEmail,
|
||||||
|
string friendlyName, Guid sponsorshipId, Guid currentUserId, SutProvider<CreateSponsorshipCommand> sutProvider)
|
||||||
|
{
|
||||||
|
sponsoringOrg.PlanType = PlanType.EnterpriseAnnually;
|
||||||
|
sponsoringOrgUser.Status = OrganizationUserStatusType.Confirmed;
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(sponsoringOrgUser.UserId.Value).Returns(user);
|
||||||
|
sutProvider.GetDependency<IOrganizationSponsorshipRepository>().WhenForAnyArgs(x => x.UpsertAsync(default)).Do(callInfo =>
|
||||||
|
{
|
||||||
|
var sponsorship = callInfo.Arg<OrganizationSponsorship>();
|
||||||
|
sponsorship.Id = sponsorshipId;
|
||||||
|
});
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(currentUserId);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().Organizations.Returns([
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Id = sponsoringOrg.Id,
|
||||||
|
Permissions = new Permissions(),
|
||||||
|
Type = OrganizationUserType.Admin
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
var actual = await Assert.ThrowsAsync<UnauthorizedAccessException>(async () =>
|
||||||
|
await sutProvider.Sut.CreateSponsorshipAsync(sponsoringOrg, sponsoringOrgUser,
|
||||||
|
PlanSponsorshipType.FamiliesForEnterprise, sponsoredEmail, friendlyName));
|
||||||
|
|
||||||
|
Assert.Equal("You do not have permissions to send sponsorships on behalf of the organization.", actual.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(OrganizationUserType.User)]
|
||||||
|
public async Task CreateSponsorship_InvalidUserType_ThrowsUnauthorizedException(
|
||||||
|
OrganizationUserType organizationUserType,
|
||||||
|
Organization sponsoringOrg, OrganizationUser sponsoringOrgUser, User user, string sponsoredEmail,
|
||||||
|
string friendlyName, Guid sponsorshipId, Guid currentUserId, SutProvider<CreateSponsorshipCommand> sutProvider)
|
||||||
|
{
|
||||||
|
sponsoringOrg.PlanType = PlanType.EnterpriseAnnually;
|
||||||
|
sponsoringOrgUser.Status = OrganizationUserStatusType.Confirmed;
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(sponsoringOrgUser.UserId.Value).Returns(user);
|
||||||
|
sutProvider.GetDependency<IOrganizationSponsorshipRepository>().WhenForAnyArgs(x => x.UpsertAsync(default)).Do(callInfo =>
|
||||||
|
{
|
||||||
|
var sponsorship = callInfo.Arg<OrganizationSponsorship>();
|
||||||
|
sponsorship.Id = sponsorshipId;
|
||||||
|
});
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(currentUserId);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().Organizations.Returns([
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Id = sponsoringOrg.Id,
|
||||||
|
Permissions = new Permissions
|
||||||
|
{
|
||||||
|
ManageUsers = true,
|
||||||
|
},
|
||||||
|
Type = organizationUserType
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
var actual = await Assert.ThrowsAsync<UnauthorizedAccessException>(async () =>
|
||||||
|
await sutProvider.Sut.CreateSponsorshipAsync(sponsoringOrg, sponsoringOrgUser,
|
||||||
|
PlanSponsorshipType.FamiliesForEnterprise, sponsoredEmail, friendlyName));
|
||||||
|
|
||||||
|
Assert.Equal("You do not have permissions to send sponsorships on behalf of the organization.", actual.Message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user