diff --git a/src/Api/Controllers/OrganizationSponsorshipsController.cs b/src/Api/Controllers/OrganizationSponsorshipsController.cs index 1030493857..07c3fd5206 100644 --- a/src/Api/Controllers/OrganizationSponsorshipsController.cs +++ b/src/Api/Controllers/OrganizationSponsorshipsController.cs @@ -68,14 +68,16 @@ namespace Bit.Api.Controllers [SelfHosted(NotSelfHostedOnly = true)] public async Task PreValidateSponsorshipToken([FromQuery] string sponsorshipToken) { - return await _organizationsSponsorshipService.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email); + return (await _organizationsSponsorshipService.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email)).valid; } [HttpPost("redeem")] [SelfHosted(NotSelfHostedOnly = true)] public async Task RedeemSponsorship([FromQuery] string sponsorshipToken, [FromBody] OrganizationSponsorshipRedeemRequestModel model) { - if (!await _organizationsSponsorshipService.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email)) + var (valid, sponsorship) = await _organizationsSponsorshipService.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email); + + if (!valid) { throw new BadRequestException("Failed to parse sponsorship token."); } @@ -86,8 +88,7 @@ namespace Bit.Api.Controllers } await _organizationsSponsorshipService.SetUpSponsorshipAsync( - await _organizationSponsorshipRepository - .GetByOfferedToEmailAsync((await CurrentUser).Email), + sponsorship, // Check org to sponsor's product type await _organizationRepository.GetByIdAsync(model.SponsoredOrganizationId)); } diff --git a/src/Core/Repositories/IOrganizationSponsorshipRepository.cs b/src/Core/Repositories/IOrganizationSponsorshipRepository.cs index f6b17a6e39..3ff151e8b1 100644 --- a/src/Core/Repositories/IOrganizationSponsorshipRepository.cs +++ b/src/Core/Repositories/IOrganizationSponsorshipRepository.cs @@ -10,6 +10,5 @@ namespace Bit.Core.Repositories { Task GetBySponsoringOrganizationUserIdAsync(Guid sponsoringOrganizationUserId); Task GetBySponsoredOrganizationIdAsync(Guid sponsoredOrganizationId); - Task GetByOfferedToEmailAsync(string email); } } diff --git a/src/Core/Services/IOrganizationSponsorshipService.cs b/src/Core/Services/IOrganizationSponsorshipService.cs index 81be9cb4af..f6244d553b 100644 --- a/src/Core/Services/IOrganizationSponsorshipService.cs +++ b/src/Core/Services/IOrganizationSponsorshipService.cs @@ -7,7 +7,7 @@ namespace Bit.Core.Services { public interface IOrganizationSponsorshipService { - Task ValidateRedemptionTokenAsync(string encryptedToken, string currentUserEmail); + Task<(bool valid, OrganizationSponsorship sponsorship)> ValidateRedemptionTokenAsync(string encryptedToken, string currentUserEmail); Task OfferSponsorshipAsync(Organization sponsoringOrg, OrganizationUser sponsoringOrgUser, PlanSponsorshipType sponsorshipType, string sponsoredEmail, string friendlyName, string sponsoringUserEmail); Task ResendSponsorshipOfferAsync(Organization sponsoringOrg, OrganizationUser sponsoringOrgUser, diff --git a/src/Core/Services/Implementations/OrganizationSponsorshipService.cs b/src/Core/Services/Implementations/OrganizationSponsorshipService.cs index 19101f0725..65fecc7976 100644 --- a/src/Core/Services/Implementations/OrganizationSponsorshipService.cs +++ b/src/Core/Services/Implementations/OrganizationSponsorshipService.cs @@ -37,11 +37,11 @@ namespace Bit.Core.Services _dataProtector = dataProtectionProvider.CreateProtector("OrganizationSponsorshipServiceDataProtector"); } - public async Task ValidateRedemptionTokenAsync(string encryptedToken, string sponsoredUserEmail) + public async Task<(bool valid, OrganizationSponsorship sponsorship)> ValidateRedemptionTokenAsync(string encryptedToken, string sponsoredUserEmail) { if (!encryptedToken.StartsWith(TokenClearTextPrefix) || sponsoredUserEmail == null) { - return false; + return (false, null); } var decryptedToken = _dataProtector.Unprotect(encryptedToken[TokenClearTextPrefix.Length..]); @@ -49,7 +49,7 @@ namespace Bit.Core.Services if (dataParts.Length != 3) { - return false; + return (false, null); } if (dataParts[0].Equals(FamiliesForEnterpriseTokenName)) @@ -57,7 +57,7 @@ namespace Bit.Core.Services if (!Guid.TryParse(dataParts[1], out Guid sponsorshipId) || !Enum.TryParse(dataParts[2], true, out var sponsorshipType)) { - return false; + return (false, null); } var sponsorship = await _organizationSponsorshipRepository.GetByIdAsync(sponsorshipId); @@ -65,13 +65,13 @@ namespace Bit.Core.Services sponsorship.PlanSponsorshipType != sponsorshipType || sponsorship.OfferedToEmail != sponsoredUserEmail) { - return false; + return (false, sponsorship); } - return true; + return (true, sponsorship); } - return false; + return (false, null); } private string RedemptionToken(Guid sponsorshipId, PlanSponsorshipType sponsorshipType) => diff --git a/src/Infrastructure.Dapper/Repositories/OrganizationSponsorshipRepository.cs b/src/Infrastructure.Dapper/Repositories/OrganizationSponsorshipRepository.cs index f80d3cf7d9..90217d859a 100644 --- a/src/Infrastructure.Dapper/Repositories/OrganizationSponsorshipRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/OrganizationSponsorshipRepository.cs @@ -48,21 +48,5 @@ namespace Bit.Infrastructure.Dapper.Repositories return results.SingleOrDefault(); } } - - public async Task GetByOfferedToEmailAsync(string offeredToEmail) - { - using (var connection = new SqlConnection(ConnectionString)) - { - var results = await connection.QueryAsync( - "[dbo].[OrganizationSponsorship_ReadByOfferedToEmail]", - new - { - OfferedToEmail = offeredToEmail - }, - commandType: CommandType.StoredProcedure); - - return results.SingleOrDefault(); - } - } } } diff --git a/test/Api.Test/Controllers/OrganizationSponsorshipsControllerTests.cs b/test/Api.Test/Controllers/OrganizationSponsorshipsControllerTests.cs index be51f093ba..d925e8adcf 100644 --- a/test/Api.Test/Controllers/OrganizationSponsorshipsControllerTests.cs +++ b/test/Api.Test/Controllers/OrganizationSponsorshipsControllerTests.cs @@ -45,7 +45,7 @@ namespace Bit.Api.Test.Controllers sutProvider.GetDependency().GetUserByIdAsync(user.Id) .Returns(user); sutProvider.GetDependency().ValidateRedemptionTokenAsync(sponsorshipToken, - user.Email).Returns(false); + user.Email).Returns((false, null)); var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.RedeemSponsorship(sponsorshipToken, model)); @@ -59,13 +59,14 @@ namespace Bit.Api.Test.Controllers [Theory] [BitAutoData] public async Task RedeemSponsorship_NotSponsoredOrgOwner_ThrowsBadRequest(string sponsorshipToken, User user, - OrganizationSponsorshipRedeemRequestModel model, SutProvider sutProvider) + OrganizationSponsorship sponsorship, OrganizationSponsorshipRedeemRequestModel model, + SutProvider sutProvider) { sutProvider.GetDependency().UserId.Returns(user.Id); sutProvider.GetDependency().GetUserByIdAsync(user.Id) .Returns(user); sutProvider.GetDependency().ValidateRedemptionTokenAsync(sponsorshipToken, - user.Email).Returns(true); + user.Email).Returns((true, sponsorship)); sutProvider.GetDependency().OrganizationOwner(model.SponsoredOrganizationId).Returns(false); var exception = await Assert.ThrowsAsync(() => @@ -79,12 +80,34 @@ namespace Bit.Api.Test.Controllers [Theory] [BitAutoData] - public async Task PreValidateSponsorshipToken_ValidatesToken_Success(string sponsorshipToken, User user, - SutProvider sutProvider) + public async Task RedeemSponsorship_NotSponsoredOrgOwner_Success(string sponsorshipToken, User user, + OrganizationSponsorship sponsorship, Organization sponsoringOrganization, + OrganizationSponsorshipRedeemRequestModel model, SutProvider sutProvider) { sutProvider.GetDependency().UserId.Returns(user.Id); sutProvider.GetDependency().GetUserByIdAsync(user.Id) .Returns(user); + sutProvider.GetDependency().ValidateRedemptionTokenAsync(sponsorshipToken, + user.Email).Returns((true, sponsorship)); + sutProvider.GetDependency().OrganizationOwner(model.SponsoredOrganizationId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(model.SponsoredOrganizationId).Returns(sponsoringOrganization); + + await sutProvider.Sut.RedeemSponsorship(sponsorshipToken, model); + + await sutProvider.GetDependency().Received(1) + .SetUpSponsorshipAsync(sponsorship, sponsoringOrganization); + } + + [Theory] + [BitAutoData] + public async Task PreValidateSponsorshipToken_ValidatesToken_Success(string sponsorshipToken, User user, + OrganizationSponsorship sponsorship, SutProvider sutProvider) + { + sutProvider.GetDependency().UserId.Returns(user.Id); + sutProvider.GetDependency().GetUserByIdAsync(user.Id) + .Returns(user); + sutProvider.GetDependency() + .ValidateRedemptionTokenAsync(sponsorshipToken, user.Email).Returns((true, sponsorship)); await sutProvider.Sut.PreValidateSponsorshipToken(sponsorshipToken);