diff --git a/src/Admin/Controllers/OrganizationsController.cs b/src/Admin/Controllers/OrganizationsController.cs index a568f03d2..6752fed89 100644 --- a/src/Admin/Controllers/OrganizationsController.cs +++ b/src/Admin/Controllers/OrganizationsController.cs @@ -14,6 +14,7 @@ using Bit.Core.Settings; using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; namespace Bit.Admin.Controllers { @@ -34,6 +35,7 @@ namespace Bit.Admin.Controllers private readonly GlobalSettings _globalSettings; private readonly IReferenceEventService _referenceEventService; private readonly IUserService _userService; + private readonly ILogger _logger; public OrganizationsController( IOrganizationRepository organizationRepository, @@ -49,7 +51,8 @@ namespace Bit.Admin.Controllers IApplicationCacheService applicationCacheService, GlobalSettings globalSettings, IReferenceEventService referenceEventService, - IUserService userService) + IUserService userService, + ILogger logger) { _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; @@ -65,6 +68,7 @@ namespace Bit.Admin.Controllers _globalSettings = globalSettings; _referenceEventService = referenceEventService; _userService = userService; + _logger = logger; } public async Task Index(string name = null, string userEmail = null, bool? paid = null, @@ -199,6 +203,7 @@ namespace Bit.Admin.Controllers catch (Exception ex) { TempData["ConnectionError"] = ex.Message; + _logger.LogWarning(ex, "Error while attempting to do billing sync for organization with id '{OrganizationId}'", id); } if (_globalSettings.SelfHosted) diff --git a/src/Api/Controllers/OrganizationConnectionsController.cs b/src/Api/Controllers/OrganizationConnectionsController.cs index ff695252c..9f9090d57 100644 --- a/src/Api/Controllers/OrganizationConnectionsController.cs +++ b/src/Api/Controllers/OrganizationConnectionsController.cs @@ -74,6 +74,10 @@ namespace Bit.Api.Controllers case OrganizationConnectionType.CloudBillingSync: var typedModel = new OrganizationConnectionRequestModel(model); var license = await _licensingService.ReadOrganizationLicenseAsync(model.OrganizationId); + if (!_licensingService.VerifyLicense(license)) + { + throw new BadRequestException("Cannot verify license file."); + } typedModel.ParsedConfig.CloudOrganizationId = license.Id; var connection = await _createOrganizationConnectionCommand.CreateAsync(typedModel.ToData()); return new OrganizationConnectionResponseModel(connection, typeof(BillingSyncConfig)); diff --git a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/SelfHosted/SelfHostedSyncSponsorshipsCommand.cs b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/SelfHosted/SelfHostedSyncSponsorshipsCommand.cs index f89c490ca..848d52012 100644 --- a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/SelfHosted/SelfHostedSyncSponsorshipsCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/SelfHosted/SelfHostedSyncSponsorshipsCommand.cs @@ -87,6 +87,7 @@ namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnte if (response == null) { + _logger.LogDebug("Organization sync failed for '{OrgId}'", organizationId); throw new BadRequestException("Organization sync failed"); } diff --git a/src/Core/Services/Implementations/BaseIdentityClientService.cs b/src/Core/Services/Implementations/BaseIdentityClientService.cs index 61ed13d01..a341b2820 100644 --- a/src/Core/Services/Implementations/BaseIdentityClientService.cs +++ b/src/Core/Services/Implementations/BaseIdentityClientService.cs @@ -124,11 +124,19 @@ namespace Bit.Core.Services if (!response.IsSuccessStatusCode) { + _logger.LogInformation("Unsuccessful token response with status code {StatusCode}", response.StatusCode); + if (response.StatusCode == HttpStatusCode.BadRequest) { _nextAuthAttempt = DateTime.UtcNow.AddDays(1); } + if (_logger.IsEnabled(LogLevel.Debug)) + { + var responseBody = await response.Content.ReadAsStringAsync(); + _logger.LogDebug("Error response body:\n{ResponseBody}", responseBody); + } + return false; } diff --git a/test/Api.Test/Controllers/OrganizationConnectionsControllerTests.cs b/test/Api.Test/Controllers/OrganizationConnectionsControllerTests.cs index 7d1268460..f7fa3fca3 100644 --- a/test/Api.Test/Controllers/OrganizationConnectionsControllerTests.cs +++ b/test/Api.Test/Controllers/OrganizationConnectionsControllerTests.cs @@ -83,9 +83,39 @@ namespace Bit.Api.Test.Controllers [Theory] [BitAutoData] - public async Task CreateConnection_Success(OrganizationConnectionRequestModel model, BillingSyncConfig config, - Guid cloudOrgId, SutProvider sutProvider) + public async Task CreateConnection_BillingSyncType_InvalidLicense_Throws(OrganizationConnectionRequestModel model, + BillingSyncConfig config, Guid cloudOrgId, OrganizationLicense organizationLicense, + SutProvider sutProvider) { + model.Type = OrganizationConnectionType.CloudBillingSync; + organizationLicense.Id = cloudOrgId; + + model.Config = JsonDocumentFromObject(config); + var typedModel = new OrganizationConnectionRequestModel(model); + typedModel.ParsedConfig.CloudOrganizationId = cloudOrgId; + + sutProvider.GetDependency() + .OrganizationOwner(model.OrganizationId) + .Returns(true); + + sutProvider.GetDependency() + .ReadOrganizationLicenseAsync(model.OrganizationId) + .Returns(organizationLicense); + + sutProvider.GetDependency() + .VerifyLicense(organizationLicense) + .Returns(false); + + await Assert.ThrowsAsync(async () => await sutProvider.Sut.CreateConnection(model)); + } + + [Theory] + [BitAutoData] + public async Task CreateConnection_Success(OrganizationConnectionRequestModel model, BillingSyncConfig config, + Guid cloudOrgId, OrganizationLicense organizationLicense, SutProvider sutProvider) + { + organizationLicense.Id = cloudOrgId; + model.Config = JsonDocumentFromObject(config); var typedModel = new OrganizationConnectionRequestModel(model); typedModel.ParsedConfig.CloudOrganizationId = cloudOrgId; @@ -95,10 +125,11 @@ namespace Bit.Api.Test.Controllers sutProvider.GetDependency().OrganizationOwner(model.OrganizationId).Returns(true); sutProvider.GetDependency() .ReadOrganizationLicenseAsync(Arg.Any()) - .Returns(new OrganizationLicense - { - Id = cloudOrgId, - }); + .Returns(organizationLicense); + + sutProvider.GetDependency() + .VerifyLicense(organizationLicense) + .Returns(true); await sutProvider.Sut.CreateConnection(model);