From 863553182991ae1c68f0580da46689a25eea92bd Mon Sep 17 00:00:00 2001 From: Rui Tome Date: Thu, 5 Oct 2023 16:31:44 +0100 Subject: [PATCH 1/2] =?UTF-8?q?[AC-1654]=C2=A0Added=20IOrganizationConnect?= =?UTF-8?q?ionRepository.GetByIdOrganizationIdAsync=20and=20modified=20Org?= =?UTF-8?q?anizationConnectionsController=20to=20use=20it=20to=20get=20a?= =?UTF-8?q?=20connection=20matching=20both=20Id=20and=20OrganizationId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OrganizationConnectionsController.cs | 2 +- .../IOrganizationConnectionRepository.cs | 1 + .../OrganizationConnectionRepository.cs | 17 +++++++++++++++++ .../OrganizationConnectionRepository.cs | 11 +++++++++++ ...izationConnection_ReadByIdOrganizationId.sql | 15 +++++++++++++++ ...023-10-05_00_OrgConnectionsReadByIdOrgId.sql | 16 ++++++++++++++++ 6 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/Sql/dbo/Stored Procedures/OrganizationConnection_ReadByIdOrganizationId.sql create mode 100644 util/Migrator/DbScripts/2023-10-05_00_OrgConnectionsReadByIdOrgId.sql diff --git a/src/Api/Controllers/OrganizationConnectionsController.cs b/src/Api/Controllers/OrganizationConnectionsController.cs index 4c04a24973fb..cd36183268f2 100644 --- a/src/Api/Controllers/OrganizationConnectionsController.cs +++ b/src/Api/Controllers/OrganizationConnectionsController.cs @@ -78,7 +78,7 @@ public async Task CreateConnection([FromBod [HttpPut("{organizationConnectionId}")] public async Task UpdateConnection(Guid organizationConnectionId, [FromBody] OrganizationConnectionRequestModel model) { - var existingOrganizationConnection = await _organizationConnectionRepository.GetByIdAsync(organizationConnectionId); + var existingOrganizationConnection = await _organizationConnectionRepository.GetByIdOrganizationIdAsync(organizationConnectionId, model.OrganizationId); if (existingOrganizationConnection == null) { throw new NotFoundException(); diff --git a/src/Core/Repositories/IOrganizationConnectionRepository.cs b/src/Core/Repositories/IOrganizationConnectionRepository.cs index a3bdbb0370b7..b480333f634c 100644 --- a/src/Core/Repositories/IOrganizationConnectionRepository.cs +++ b/src/Core/Repositories/IOrganizationConnectionRepository.cs @@ -5,6 +5,7 @@ namespace Bit.Core.Repositories; public interface IOrganizationConnectionRepository : IRepository { + Task GetByIdOrganizationIdAsync(Guid id, Guid organizationId); Task> GetByOrganizationIdTypeAsync(Guid organizationId, OrganizationConnectionType type); Task> GetEnabledByOrganizationIdTypeAsync(Guid organizationId, OrganizationConnectionType type); } diff --git a/src/Infrastructure.Dapper/Repositories/OrganizationConnectionRepository.cs b/src/Infrastructure.Dapper/Repositories/OrganizationConnectionRepository.cs index 7ea1c72272a4..0168f57e32fc 100644 --- a/src/Infrastructure.Dapper/Repositories/OrganizationConnectionRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/OrganizationConnectionRepository.cs @@ -14,6 +14,23 @@ public OrganizationConnectionRepository(GlobalSettings globalSettings) : base(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString) { } + public async Task GetByIdOrganizationIdAsync(Guid id, Guid organizationId) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + $"[{Schema}].[OrganizationConnection_ReadByIdOrganizationId]", + new + { + Id = id, + OrganizationId = organizationId + }, + commandType: CommandType.StoredProcedure); + + return results.FirstOrDefault(); + } + } + public async Task> GetByOrganizationIdTypeAsync(Guid organizationId, OrganizationConnectionType type) { using (var connection = new SqlConnection(ConnectionString)) diff --git a/src/Infrastructure.EntityFramework/Repositories/OrganizationConnectionRepository.cs b/src/Infrastructure.EntityFramework/Repositories/OrganizationConnectionRepository.cs index 298e28e02926..ad8359758bb5 100644 --- a/src/Infrastructure.EntityFramework/Repositories/OrganizationConnectionRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/OrganizationConnectionRepository.cs @@ -15,6 +15,17 @@ public OrganizationConnectionRepository(IServiceScopeFactory serviceScopeFactory { } + public async Task GetByIdOrganizationIdAsync(Guid id, Guid organizationId) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var connection = await dbContext.OrganizationConnections + .FirstOrDefaultAsync(oc => oc.Id == id && oc.OrganizationId == organizationId); + return Mapper.Map(connection); + } + } + public async Task> GetByOrganizationIdTypeAsync(Guid organizationId, OrganizationConnectionType type) { using (var scope = ServiceScopeFactory.CreateScope()) diff --git a/src/Sql/dbo/Stored Procedures/OrganizationConnection_ReadByIdOrganizationId.sql b/src/Sql/dbo/Stored Procedures/OrganizationConnection_ReadByIdOrganizationId.sql new file mode 100644 index 000000000000..42c41f7e117a --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationConnection_ReadByIdOrganizationId.sql @@ -0,0 +1,15 @@ +CREATE PROCEDURE [dbo].[OrganizationConnection_ReadByIdOrganizationId] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[OrganizationConnectionView] + WHERE + [Id] = @Id AND + [OrganizationId] = @OrganizationId +END diff --git a/util/Migrator/DbScripts/2023-10-05_00_OrgConnectionsReadByIdOrgId.sql b/util/Migrator/DbScripts/2023-10-05_00_OrgConnectionsReadByIdOrgId.sql new file mode 100644 index 000000000000..34c9503086e8 --- /dev/null +++ b/util/Migrator/DbScripts/2023-10-05_00_OrgConnectionsReadByIdOrgId.sql @@ -0,0 +1,16 @@ +CREATE OR ALTER PROCEDURE [dbo].[OrganizationConnection_ReadByIdOrganizationId] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationConnectionView] +WHERE + [Id] = @Id AND + [OrganizationId] = @OrganizationId +END +GO From d5e58feba942e67fe6a2270e0a4aaf473b51d9c0 Mon Sep 17 00:00:00 2001 From: Rui Tome Date: Thu, 5 Oct 2023 16:48:48 +0100 Subject: [PATCH 2/2] =?UTF-8?q?[AC-1654]=C2=A0Fixed=20unit=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OrganizationConnectionsController.cs | 5 +++++ .../OrganizationConnectionsControllerTests.cs | 16 +++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Api/Controllers/OrganizationConnectionsController.cs b/src/Api/Controllers/OrganizationConnectionsController.cs index cd36183268f2..e9ea31c8ffca 100644 --- a/src/Api/Controllers/OrganizationConnectionsController.cs +++ b/src/Api/Controllers/OrganizationConnectionsController.cs @@ -78,6 +78,11 @@ public async Task CreateConnection([FromBod [HttpPut("{organizationConnectionId}")] public async Task UpdateConnection(Guid organizationConnectionId, [FromBody] OrganizationConnectionRequestModel model) { + if (model == null) + { + throw new NotFoundException(); + } + var existingOrganizationConnection = await _organizationConnectionRepository.GetByIdOrganizationIdAsync(organizationConnectionId, model.OrganizationId); if (existingOrganizationConnection == null) { diff --git a/test/Api.Test/Controllers/OrganizationConnectionsControllerTests.cs b/test/Api.Test/Controllers/OrganizationConnectionsControllerTests.cs index c95d6861f6c0..bbeb50b18223 100644 --- a/test/Api.Test/Controllers/OrganizationConnectionsControllerTests.cs +++ b/test/Api.Test/Controllers/OrganizationConnectionsControllerTests.cs @@ -143,10 +143,10 @@ await sutProvider.GetDependency().Received public async Task UpdateConnection_RequiresOwnerPermissions(SutProvider sutProvider) { sutProvider.GetDependency() - .GetByIdAsync(Arg.Any()) + .GetByIdOrganizationIdAsync(Arg.Any(), Arg.Any()) .Returns(new OrganizationConnection()); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateConnection(default, null)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateConnection(default, new OrganizationConnectionRequestModel())); Assert.Contains("You do not have permission to update this connection.", exception.Message); } @@ -164,8 +164,8 @@ public async Task UpdateConnection_BillingSync_OnlyOneConnectionOfEachType(Organ sutProvider.GetDependency().OrganizationOwner(typedModel.OrganizationId).Returns(true); var orgConnectionRepository = sutProvider.GetDependency(); - orgConnectionRepository.GetByIdAsync(existing1.Id).Returns(existing1); - orgConnectionRepository.GetByIdAsync(existing2.Id).Returns(existing2); + orgConnectionRepository.GetByIdOrganizationIdAsync(existing1.Id, existing1.OrganizationId).Returns(existing1); + orgConnectionRepository.GetByIdOrganizationIdAsync(existing2.Id, existing2.OrganizationId).Returns(existing2); orgConnectionRepository.GetByOrganizationIdTypeAsync(typedModel.OrganizationId, type).Returns(new[] { existing1, existing2 }); var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateConnection(existing1.Id, typedModel)); @@ -186,7 +186,7 @@ public async Task UpdateConnection_Scim_OnlyOneConnectionOfEachType(Organization sutProvider.GetDependency().OrganizationOwner(typedModel.OrganizationId).Returns(true); sutProvider.GetDependency() - .GetByIdAsync(existing1.Id) + .GetByIdOrganizationIdAsync(existing1.Id, existing1.OrganizationId) .Returns(existing1); sutProvider.GetDependency().ManageScim(typedModel.OrganizationId).Returns(true); @@ -212,6 +212,7 @@ public async Task UpdateConnection_Success(OrganizationConnection existing, Bill }); updated.Config = JsonSerializer.Serialize(config); updated.Id = existing.Id; + updated.OrganizationId = existing.OrganizationId; updated.Type = OrganizationConnectionType.CloudBillingSync; var model = RequestModelFromEntity(updated); @@ -224,7 +225,7 @@ public async Task UpdateConnection_Success(OrganizationConnection existing, Bill .UpdateAsync(default) .ReturnsForAnyArgs(updated); sutProvider.GetDependency() - .GetByIdAsync(existing.Id) + .GetByIdOrganizationIdAsync(existing.Id, existing.OrganizationId) .Returns(existing); OrganizationLicense organizationLicense = new OrganizationLicense(); @@ -264,6 +265,7 @@ public async Task UpdateConnection_BillingSyncType_InvalidLicense_ErrorThrows(Or }); updated.Config = JsonSerializer.Serialize(config); updated.Id = existing.Id; + updated.OrganizationId = existing.OrganizationId; updated.Type = OrganizationConnectionType.CloudBillingSync; var model = RequestModelFromEntity(updated); sutProvider.GetDependency().SelfHosted.Returns(true); @@ -275,7 +277,7 @@ public async Task UpdateConnection_BillingSyncType_InvalidLicense_ErrorThrows(Or .UpdateAsync(default) .ReturnsForAnyArgs(updated); sutProvider.GetDependency() - .GetByIdAsync(existing.Id) + .GetByIdOrganizationIdAsync(existing.Id, existing.OrganizationId) .Returns(existing); OrganizationLicense organizationLicense = new OrganizationLicense();