From 39cd2756ded0b81c772ce6a13368c0e0bb1d99ad Mon Sep 17 00:00:00 2001 From: jrmccannon Date: Thu, 26 Sep 2024 15:48:28 -0500 Subject: [PATCH 01/12] Added /domain/verified to organization controller --- .../OrganizationDomainController.cs | 18 +++++++++++++ ...rganizationDomainSsoDetailResponseModel.cs | 26 +++++++++++++++++++ ...ganizationDomainSsoDetailsResponseModel.cs | 8 ++++++ ...VerifiedOrganizationDomainSsoDetailData.cs | 26 +++++++++++++++++++ .../IOrganizationDomainRepository.cs | 1 + .../OrganizationDomainRepository.cs | 11 ++++++++ .../OrganizationDomainRepository.cs | 22 ++++++++++++++++ ...ganizationDomainSsoDetails_ReadByEmail.sql | 24 +++++++++++++++++ 8 files changed, 136 insertions(+) create mode 100644 src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailResponseModel.cs create mode 100644 src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailsResponseModel.cs create mode 100644 src/Core/Models/Data/Organizations/VerifiedOrganizationDomainSsoDetailData.cs create mode 100644 util/Migrator/DbScripts/2024-09-26_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql diff --git a/src/Api/AdminConsole/Controllers/OrganizationDomainController.cs b/src/Api/AdminConsole/Controllers/OrganizationDomainController.cs index 35c927d5a95f..fb6a01062313 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationDomainController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationDomainController.cs @@ -133,6 +133,24 @@ public async Task GetOrgDomainSsoDeta return new OrganizationDomainSsoDetailsResponseModel(ssoResult); } + [AllowAnonymous] + [HttpPost("domain/verified")] + public async Task GetVerifiedOrgDomainSsoDetailsAsync( + [FromBody] OrganizationDomainSsoDetailsRequestModel model) + { + var ssoResults = (await _organizationDomainRepository + .GetVerifiedOrganizationDomainSsoDetailsAsync(model.Email)) + .ToList(); + + if (ssoResults is null || ssoResults.Count == 0) + { + throw new NotFoundException("Claimed org domain not found"); + } + + return new VerifiedOrganizationDomainSsoDetailsResponseModel( + ssoResults.Select(ssoResult => new VerifiedOrganizationDomainSsoDetailResponseModel(ssoResult))); + } + private async Task ValidateOrganizationAccessAsync(Guid orgIdGuid) { if (!await _currentContext.ManageSso(orgIdGuid)) diff --git a/src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailResponseModel.cs new file mode 100644 index 000000000000..eb64e917242a --- /dev/null +++ b/src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailResponseModel.cs @@ -0,0 +1,26 @@ +using Bit.Core.Models.Api; +using Bit.Core.Models.Data.Organizations; + +namespace Bit.Api.AdminConsole.Models.Response.Organizations; + +public class VerifiedOrganizationDomainSsoDetailResponseModel : ResponseModel +{ + public VerifiedOrganizationDomainSsoDetailResponseModel(VerifiedOrganizationDomainSsoDetailData data) + : base("verifiedOrganizationDomainSsoDetails") + { + if (data is null) + { + throw new ArgumentNullException(nameof(data)); + } + + SsoAvailable = data.SsoAvailable; + DomainName = data.DomainName; + OrganizationIdentifier = data.OrganizationIdentifier; + VerifiedDate = data.VerifiedDate; + } + + public bool SsoAvailable { get; } + public string DomainName { get; } + public string OrganizationIdentifier { get; } + public DateTime VerifiedDate { get; } +} diff --git a/src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailsResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailsResponseModel.cs new file mode 100644 index 000000000000..3488eab2c87b --- /dev/null +++ b/src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailsResponseModel.cs @@ -0,0 +1,8 @@ +using Bit.Api.Models.Response; + +namespace Bit.Api.AdminConsole.Models.Response.Organizations; + +public class VerifiedOrganizationDomainSsoDetailsResponseModel( + IEnumerable data, + string continuationToken = null) + : ListResponseModel(data, continuationToken); diff --git a/src/Core/Models/Data/Organizations/VerifiedOrganizationDomainSsoDetailData.cs b/src/Core/Models/Data/Organizations/VerifiedOrganizationDomainSsoDetailData.cs new file mode 100644 index 000000000000..8d8383423c53 --- /dev/null +++ b/src/Core/Models/Data/Organizations/VerifiedOrganizationDomainSsoDetailData.cs @@ -0,0 +1,26 @@ +namespace Bit.Core.Models.Data.Organizations; + +public class VerifiedOrganizationDomainSsoDetailData +{ + public VerifiedOrganizationDomainSsoDetailData() + { + } + + public VerifiedOrganizationDomainSsoDetailData(Guid organizationId, string organizationName, string domainName, + bool ssoAvailable, string organizationIdentifier, DateTime verifiedDate) + { + OrganizationId = organizationId; + OrganizationName = organizationName; + DomainName = domainName; + SsoAvailable = ssoAvailable; + OrganizationIdentifier = organizationIdentifier; + VerifiedDate = verifiedDate; + } + + public Guid OrganizationId { get; init; } + public string OrganizationName { get; init; } + public string DomainName { get; init; } + public bool SsoAvailable { get; init; } + public string OrganizationIdentifier { get; init; } + public DateTime VerifiedDate { get; init; } +} diff --git a/src/Core/Repositories/IOrganizationDomainRepository.cs b/src/Core/Repositories/IOrganizationDomainRepository.cs index 3fde08a54d73..4c1c9c25dd9f 100644 --- a/src/Core/Repositories/IOrganizationDomainRepository.cs +++ b/src/Core/Repositories/IOrganizationDomainRepository.cs @@ -11,6 +11,7 @@ public interface IOrganizationDomainRepository : IRepository> GetDomainsByOrganizationIdAsync(Guid orgId); Task> GetManyByNextRunDateAsync(DateTime date); Task GetOrganizationDomainSsoDetailsAsync(string email); + Task> GetVerifiedOrganizationDomainSsoDetailsAsync(string email); Task GetDomainByIdOrganizationIdAsync(Guid id, Guid organizationId); Task GetDomainByOrgIdAndDomainNameAsync(Guid orgId, string domainName); Task> GetExpiredOrganizationDomainsAsync(); diff --git a/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs b/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs index 31d599f0cc56..9ecdd56a3e99 100644 --- a/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs @@ -71,6 +71,17 @@ public async Task> GetManyByNextRunDateAsync(Dat } } + public async Task> GetVerifiedOrganizationDomainSsoDetailsAsync(string email) + { + await using var connection = new SqlConnection(ConnectionString); + + return await connection + .QueryAsync( + $"[{Schema}].[VerifiedOrganizationDomainSsoDetails_ReadByEmail]", + new { Email = email }, + commandType: CommandType.StoredProcedure); + } + public async Task GetDomainByIdOrganizationIdAsync(Guid id, Guid orgId) { using (var connection = new SqlConnection(ConnectionString)) diff --git a/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs b/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs index 9135c8bd1d78..2b03dd5e07a1 100644 --- a/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs @@ -95,6 +95,28 @@ from s in sJoin.DefaultIfEmpty() return ssoDetails; } + public async Task> GetVerifiedOrganizationDomainSsoDetailsAsync(string email) + { + var domainName = new MailAddress(email).Host; + + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + return await (from o in dbContext.Organizations + from od in o.Domains + join s in dbContext.SsoConfigs on o.Id equals s.OrganizationId into sJoin + from s in sJoin.DefaultIfEmpty() + where od.DomainName == domainName && o.Enabled + select new VerifiedOrganizationDomainSsoDetailData( + o.Id, + o.Name, + od.DomainName, + o.SsoConfigs.Any(sc => sc.Enabled), + o.Identifier, + od.VerifiedDate ?? DateTime.UtcNow)) + .AsNoTracking() + .ToListAsync(); + } + public async Task GetDomainByIdOrganizationIdAsync(Guid id, Guid orgId) { using var scope = ServiceScopeFactory.CreateScope(); diff --git a/util/Migrator/DbScripts/2024-09-26_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql b/util/Migrator/DbScripts/2024-09-26_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql new file mode 100644 index 000000000000..78f031fbcb5e --- /dev/null +++ b/util/Migrator/DbScripts/2024-09-26_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql @@ -0,0 +1,24 @@ +CREATE OR ALTER PROCEDURE [dbo].[VerifiedOrganizationDomainSsoDetails_ReadByEmail] + @Email NVARCHAR(256) +AS +BEGIN + SET NOCOUNT ON + + DECLARE @Domain NVARCHAR(256) + +SELECT @Domain = SUBSTRING(@Email, CHARINDEX( '@', @Email) + 1, LEN(@Email)) + +SELECT + O.Id AS OrganizationId, + O.[Name] AS OrganizationName, + S.Enabled AS SsoAvailable, + O.Identifier AS OrganizationIdentifier, + OD.VerifiedDate, + OD.DomainName +FROM [dbo].[OrganizationView] O + INNER JOIN [dbo].[OrganizationDomainView] OD ON O.Id = OD.OrganizationId + LEFT JOIN [dbo].[Ssoconfig] S ON O.Id = S.OrganizationId +WHERE OD.DomainName = @Domain + AND O.Enabled = 1 + AND OD.VerifiedDate IS NOT NULL +END From abb6b539d4d90f8d9a45b9695219c03a2cb9c3a8 Mon Sep 17 00:00:00 2001 From: jrmccannon Date: Thu, 26 Sep 2024 16:04:24 -0500 Subject: [PATCH 02/12] Added a couple tests similar to the orgdomain sso details --- .../OrganizationDomainControllerTests.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationDomainControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationDomainControllerTests.cs index 2f74303415ed..0d45ef67417a 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationDomainControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationDomainControllerTests.cs @@ -316,4 +316,26 @@ public async Task GetOrgDomainSsoDetails_ShouldReturnOrganizationDomainSsoDetail Assert.IsType(result); } + + [Theory, BitAutoData] + public async Task GetVerifiedOrgDomainSsoDetails_ShouldThrowNotFound_WhenEmailHasNotClaimedDomain( + OrganizationDomainSsoDetailsRequestModel model, SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetVerifiedOrganizationDomainSsoDetailsAsync(model.Email).Returns(Array.Empty()); + + await Assert.ThrowsAsync(() => sutProvider.Sut.GetOrgDomainSsoDetails(model)); + } + + [Theory, BitAutoData] + public async Task GetVerifiedOrgDomainSsoDetails_ShouldReturnOrganizationDomainSsoDetails_WhenEmailHasClaimedDomain( + OrganizationDomainSsoDetailsRequestModel model, IEnumerable ssoDetailsData, SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetVerifiedOrganizationDomainSsoDetailsAsync(model.Email).Returns(ssoDetailsData); + + var result = await sutProvider.Sut.GetVerifiedOrgDomainSsoDetailsAsync(model); + + Assert.IsType(result); + } } From 98484e9fdc1c12f71166c0d352ea366d7b33dfac Mon Sep 17 00:00:00 2001 From: jrmccannon Date: Mon, 30 Sep 2024 09:41:38 -0500 Subject: [PATCH 03/12] Trimmed down verified org domain sso detail. Fixed up name. --- ...rganizationDomainSsoDetailResponseModel.cs | 7 +---- .../VerifiedOrganizationDomainSsoDetail.cs | 22 ++++++++++++++++ ...VerifiedOrganizationDomainSsoDetailData.cs | 26 ------------------- .../IOrganizationDomainRepository.cs | 2 +- .../OrganizationDomainRepository.cs | 4 +-- .../OrganizationDomainRepository.cs | 13 +++++----- .../OrganizationDomainControllerTests.cs | 4 +-- ...ganizationDomainSsoDetails_ReadByEmail.sql | 4 +-- 8 files changed, 36 insertions(+), 46 deletions(-) create mode 100644 src/Core/Models/Data/Organizations/VerifiedOrganizationDomainSsoDetail.cs delete mode 100644 src/Core/Models/Data/Organizations/VerifiedOrganizationDomainSsoDetailData.cs diff --git a/src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailResponseModel.cs index eb64e917242a..d8516d31989c 100644 --- a/src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailResponseModel.cs @@ -5,7 +5,7 @@ namespace Bit.Api.AdminConsole.Models.Response.Organizations; public class VerifiedOrganizationDomainSsoDetailResponseModel : ResponseModel { - public VerifiedOrganizationDomainSsoDetailResponseModel(VerifiedOrganizationDomainSsoDetailData data) + public VerifiedOrganizationDomainSsoDetailResponseModel(VerifiedOrganizationDomainSsoDetail data) : base("verifiedOrganizationDomainSsoDetails") { if (data is null) @@ -13,14 +13,9 @@ public VerifiedOrganizationDomainSsoDetailResponseModel(VerifiedOrganizationDoma throw new ArgumentNullException(nameof(data)); } - SsoAvailable = data.SsoAvailable; DomainName = data.DomainName; OrganizationIdentifier = data.OrganizationIdentifier; - VerifiedDate = data.VerifiedDate; } - - public bool SsoAvailable { get; } public string DomainName { get; } public string OrganizationIdentifier { get; } - public DateTime VerifiedDate { get; } } diff --git a/src/Core/Models/Data/Organizations/VerifiedOrganizationDomainSsoDetail.cs b/src/Core/Models/Data/Organizations/VerifiedOrganizationDomainSsoDetail.cs new file mode 100644 index 000000000000..0a07af66b8e3 --- /dev/null +++ b/src/Core/Models/Data/Organizations/VerifiedOrganizationDomainSsoDetail.cs @@ -0,0 +1,22 @@ +namespace Bit.Core.Models.Data.Organizations; + +public class VerifiedOrganizationDomainSsoDetail +{ + public VerifiedOrganizationDomainSsoDetail() + { + } + + public VerifiedOrganizationDomainSsoDetail(Guid organizationId, string organizationName, string domainName, + string organizationIdentifier) + { + OrganizationId = organizationId; + OrganizationName = organizationName; + DomainName = domainName; + OrganizationIdentifier = organizationIdentifier; + } + + public Guid OrganizationId { get; init; } + public string OrganizationName { get; init; } + public string DomainName { get; init; } + public string OrganizationIdentifier { get; init; } +} diff --git a/src/Core/Models/Data/Organizations/VerifiedOrganizationDomainSsoDetailData.cs b/src/Core/Models/Data/Organizations/VerifiedOrganizationDomainSsoDetailData.cs deleted file mode 100644 index 8d8383423c53..000000000000 --- a/src/Core/Models/Data/Organizations/VerifiedOrganizationDomainSsoDetailData.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Bit.Core.Models.Data.Organizations; - -public class VerifiedOrganizationDomainSsoDetailData -{ - public VerifiedOrganizationDomainSsoDetailData() - { - } - - public VerifiedOrganizationDomainSsoDetailData(Guid organizationId, string organizationName, string domainName, - bool ssoAvailable, string organizationIdentifier, DateTime verifiedDate) - { - OrganizationId = organizationId; - OrganizationName = organizationName; - DomainName = domainName; - SsoAvailable = ssoAvailable; - OrganizationIdentifier = organizationIdentifier; - VerifiedDate = verifiedDate; - } - - public Guid OrganizationId { get; init; } - public string OrganizationName { get; init; } - public string DomainName { get; init; } - public bool SsoAvailable { get; init; } - public string OrganizationIdentifier { get; init; } - public DateTime VerifiedDate { get; init; } -} diff --git a/src/Core/Repositories/IOrganizationDomainRepository.cs b/src/Core/Repositories/IOrganizationDomainRepository.cs index 4c1c9c25dd9f..f8b45574a247 100644 --- a/src/Core/Repositories/IOrganizationDomainRepository.cs +++ b/src/Core/Repositories/IOrganizationDomainRepository.cs @@ -11,7 +11,7 @@ public interface IOrganizationDomainRepository : IRepository> GetDomainsByOrganizationIdAsync(Guid orgId); Task> GetManyByNextRunDateAsync(DateTime date); Task GetOrganizationDomainSsoDetailsAsync(string email); - Task> GetVerifiedOrganizationDomainSsoDetailsAsync(string email); + Task> GetVerifiedOrganizationDomainSsoDetailsAsync(string email); Task GetDomainByIdOrganizationIdAsync(Guid id, Guid organizationId); Task GetDomainByOrgIdAndDomainNameAsync(Guid orgId, string domainName); Task> GetExpiredOrganizationDomainsAsync(); diff --git a/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs b/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs index 9ecdd56a3e99..1a7085eb18d7 100644 --- a/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs @@ -71,12 +71,12 @@ public async Task> GetManyByNextRunDateAsync(Dat } } - public async Task> GetVerifiedOrganizationDomainSsoDetailsAsync(string email) + public async Task> GetVerifiedOrganizationDomainSsoDetailsAsync(string email) { await using var connection = new SqlConnection(ConnectionString); return await connection - .QueryAsync( + .QueryAsync( $"[{Schema}].[VerifiedOrganizationDomainSsoDetails_ReadByEmail]", new { Email = email }, commandType: CommandType.StoredProcedure); diff --git a/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs b/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs index 2b03dd5e07a1..3e2d6e44a42b 100644 --- a/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs @@ -95,7 +95,7 @@ from s in sJoin.DefaultIfEmpty() return ssoDetails; } - public async Task> GetVerifiedOrganizationDomainSsoDetailsAsync(string email) + public async Task> GetVerifiedOrganizationDomainSsoDetailsAsync(string email) { var domainName = new MailAddress(email).Host; @@ -105,14 +105,15 @@ public async Task> GetVerif from od in o.Domains join s in dbContext.SsoConfigs on o.Id equals s.OrganizationId into sJoin from s in sJoin.DefaultIfEmpty() - where od.DomainName == domainName && o.Enabled - select new VerifiedOrganizationDomainSsoDetailData( + where od.DomainName == domainName + && o.Enabled + && s.Enabled + && od.VerifiedDate != null + select new VerifiedOrganizationDomainSsoDetail( o.Id, o.Name, od.DomainName, - o.SsoConfigs.Any(sc => sc.Enabled), - o.Identifier, - od.VerifiedDate ?? DateTime.UtcNow)) + o.Identifier)) .AsNoTracking() .ToListAsync(); } diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationDomainControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationDomainControllerTests.cs index 0d45ef67417a..1ff4b519c095 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationDomainControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationDomainControllerTests.cs @@ -322,14 +322,14 @@ public async Task GetVerifiedOrgDomainSsoDetails_ShouldThrowNotFound_WhenEmailHa OrganizationDomainSsoDetailsRequestModel model, SutProvider sutProvider) { sutProvider.GetDependency() - .GetVerifiedOrganizationDomainSsoDetailsAsync(model.Email).Returns(Array.Empty()); + .GetVerifiedOrganizationDomainSsoDetailsAsync(model.Email).Returns(Array.Empty()); await Assert.ThrowsAsync(() => sutProvider.Sut.GetOrgDomainSsoDetails(model)); } [Theory, BitAutoData] public async Task GetVerifiedOrgDomainSsoDetails_ShouldReturnOrganizationDomainSsoDetails_WhenEmailHasClaimedDomain( - OrganizationDomainSsoDetailsRequestModel model, IEnumerable ssoDetailsData, SutProvider sutProvider) + OrganizationDomainSsoDetailsRequestModel model, IEnumerable ssoDetailsData, SutProvider sutProvider) { sutProvider.GetDependency() .GetVerifiedOrganizationDomainSsoDetailsAsync(model.Email).Returns(ssoDetailsData); diff --git a/util/Migrator/DbScripts/2024-09-26_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql b/util/Migrator/DbScripts/2024-09-26_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql index 78f031fbcb5e..e208f7daf263 100644 --- a/util/Migrator/DbScripts/2024-09-26_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql +++ b/util/Migrator/DbScripts/2024-09-26_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql @@ -10,10 +10,7 @@ SELECT @Domain = SUBSTRING(@Email, CHARINDEX( '@', @Email) + 1, LEN(@Email)) SELECT O.Id AS OrganizationId, - O.[Name] AS OrganizationName, - S.Enabled AS SsoAvailable, O.Identifier AS OrganizationIdentifier, - OD.VerifiedDate, OD.DomainName FROM [dbo].[OrganizationView] O INNER JOIN [dbo].[OrganizationDomainView] OD ON O.Id = OD.OrganizationId @@ -21,4 +18,5 @@ FROM [dbo].[OrganizationView] O WHERE OD.DomainName = @Domain AND O.Enabled = 1 AND OD.VerifiedDate IS NOT NULL + AND (P.Id is NULL OR (P.Id IS NOT NULL AND P.[Type] = 4)) -- SSO Type END From 4e5a9e6e225c674775e1ace28f76023049c8e96f Mon Sep 17 00:00:00 2001 From: jrmccannon Date: Mon, 30 Sep 2024 09:58:56 -0500 Subject: [PATCH 04/12] Added placeholder feature flag key. --- .../AdminConsole/Controllers/OrganizationDomainController.cs | 3 +++ src/Core/Constants.cs | 1 + 2 files changed, 4 insertions(+) diff --git a/src/Api/AdminConsole/Controllers/OrganizationDomainController.cs b/src/Api/AdminConsole/Controllers/OrganizationDomainController.cs index fb6a01062313..87ee19c04414 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationDomainController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationDomainController.cs @@ -2,11 +2,13 @@ using Bit.Api.AdminConsole.Models.Request.Organizations; using Bit.Api.AdminConsole.Models.Response.Organizations; using Bit.Api.Models.Response; +using Bit.Core; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Exceptions; using Bit.Core.Repositories; +using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -135,6 +137,7 @@ public async Task GetOrgDomainSsoDeta [AllowAnonymous] [HttpPost("domain/verified")] + [RequireFeature(FeatureFlagKeys.VerifiedSsoDomainEndpoint)] public async Task GetVerifiedOrgDomainSsoDetailsAsync( [FromBody] OrganizationDomainSsoDetailsRequestModel model) { diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 3f4a16cb8d99..c8dbf205af3d 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -143,6 +143,7 @@ public static class FeatureFlagKeys public const string CipherKeyEncryption = "cipher-key-encryption"; public const string EnableNewCardCombinedExpiryAutofill = "enable-new-card-combined-expiry-autofill"; public const string StorageReseedRefactor = "storage-reseed-refactor"; + public const string VerifiedSsoDomainEndpoint = "PLACEHOLDER"; public static List GetAllKeys() { From aac397b9d1e95d3e73776052ea1f833f2f304e90 Mon Sep 17 00:00:00 2001 From: jrmccannon Date: Mon, 30 Sep 2024 18:41:00 -0500 Subject: [PATCH 05/12] Replaced placeholder with feature flag key. --- src/Core/Constants.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index c8dbf205af3d..a6bb9aa56a1b 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -143,7 +143,7 @@ public static class FeatureFlagKeys public const string CipherKeyEncryption = "cipher-key-encryption"; public const string EnableNewCardCombinedExpiryAutofill = "enable-new-card-combined-expiry-autofill"; public const string StorageReseedRefactor = "storage-reseed-refactor"; - public const string VerifiedSsoDomainEndpoint = "PLACEHOLDER"; + public const string VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint"; public static List GetAllKeys() { From c22656e7e802d2ac54120a395c1fbb6f69123bd0 Mon Sep 17 00:00:00 2001 From: jrmccannon Date: Tue, 1 Oct 2024 10:25:23 -0500 Subject: [PATCH 06/12] Adding 00 --- ...26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename util/Migrator/DbScripts/{2024-09-26_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql => 2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql} (100%) diff --git a/util/Migrator/DbScripts/2024-09-26_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql b/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql similarity index 100% rename from util/Migrator/DbScripts/2024-09-26_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql rename to util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql From fec6fb5be093fbca6141e44c345e5539f2bc0ff2 Mon Sep 17 00:00:00 2001 From: jrmccannon Date: Tue, 1 Oct 2024 10:27:50 -0500 Subject: [PATCH 07/12] Adding go to end --- ...26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql b/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql index e208f7daf263..d4952622d392 100644 --- a/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql +++ b/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql @@ -20,3 +20,4 @@ WHERE OD.DomainName = @Domain AND OD.VerifiedDate IS NOT NULL AND (P.Id is NULL OR (P.Id IS NOT NULL AND P.[Type] = 4)) -- SSO Type END +GO From 2702d3ee602f957526677bf5b38d06b31cdea598 Mon Sep 17 00:00:00 2001 From: jrmccannon Date: Tue, 1 Oct 2024 15:56:20 -0500 Subject: [PATCH 08/12] Adding left join. --- ...26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql b/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql index d4952622d392..078edd0eb71b 100644 --- a/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql +++ b/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql @@ -14,6 +14,7 @@ SELECT OD.DomainName FROM [dbo].[OrganizationView] O INNER JOIN [dbo].[OrganizationDomainView] OD ON O.Id = OD.OrganizationId + LEFT JOIN [dbo].[PolicyView] P ON O.Id = P.OrganizationId LEFT JOIN [dbo].[Ssoconfig] S ON O.Id = S.OrganizationId WHERE OD.DomainName = @Domain AND O.Enabled = 1 From b3e13982f86b20226bd875f4d1668c4473b42b55 Mon Sep 17 00:00:00 2001 From: jrmccannon Date: Wed, 2 Oct 2024 09:24:14 -0500 Subject: [PATCH 09/12] Restricting sproc to only return verified domains if the org has sso. Adding name. corrected route. removed not found exception. --- .../Controllers/OrganizationDomainController.cs | 7 +------ ...AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql | 3 ++- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Api/AdminConsole/Controllers/OrganizationDomainController.cs b/src/Api/AdminConsole/Controllers/OrganizationDomainController.cs index 87ee19c04414..af7a162d8086 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationDomainController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationDomainController.cs @@ -136,7 +136,7 @@ public async Task GetOrgDomainSsoDeta } [AllowAnonymous] - [HttpPost("domain/verified")] + [HttpPost("domain/sso/verified")] [RequireFeature(FeatureFlagKeys.VerifiedSsoDomainEndpoint)] public async Task GetVerifiedOrgDomainSsoDetailsAsync( [FromBody] OrganizationDomainSsoDetailsRequestModel model) @@ -145,11 +145,6 @@ public async Task GetVerified .GetVerifiedOrganizationDomainSsoDetailsAsync(model.Email)) .ToList(); - if (ssoResults is null || ssoResults.Count == 0) - { - throw new NotFoundException("Claimed org domain not found"); - } - return new VerifiedOrganizationDomainSsoDetailsResponseModel( ssoResults.Select(ssoResult => new VerifiedOrganizationDomainSsoDetailResponseModel(ssoResult))); } diff --git a/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql b/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql index 078edd0eb71b..910ff0e4f6ae 100644 --- a/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql +++ b/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql @@ -10,6 +10,7 @@ SELECT @Domain = SUBSTRING(@Email, CHARINDEX( '@', @Email) + 1, LEN(@Email)) SELECT O.Id AS OrganizationId, + O.Name AS OrganizationName, O.Identifier AS OrganizationIdentifier, OD.DomainName FROM [dbo].[OrganizationView] O @@ -19,6 +20,6 @@ FROM [dbo].[OrganizationView] O WHERE OD.DomainName = @Domain AND O.Enabled = 1 AND OD.VerifiedDate IS NOT NULL - AND (P.Id is NULL OR (P.Id IS NOT NULL AND P.[Type] = 4)) -- SSO Type + AND P.Id IS NOT NULL AND P.[Type] = 4 -- SSO Type END GO From 43598a218d42d7a8b75ade50ba48246dd00dc524 Mon Sep 17 00:00:00 2001 From: jrmccannon Date: Thu, 3 Oct 2024 14:41:41 -0500 Subject: [PATCH 10/12] Adding the sproc definition to the SQL project --- ...anaizationDomainSsoDetails_ReadByEmail.sql | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/Sql/dbo/Stored Procedures/VerfiedOrganaizationDomainSsoDetails_ReadByEmail.sql diff --git a/src/Sql/dbo/Stored Procedures/VerfiedOrganaizationDomainSsoDetails_ReadByEmail.sql b/src/Sql/dbo/Stored Procedures/VerfiedOrganaizationDomainSsoDetails_ReadByEmail.sql new file mode 100644 index 000000000000..ca6991d809a3 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/VerfiedOrganaizationDomainSsoDetails_ReadByEmail.sql @@ -0,0 +1,24 @@ +CREATE PROCEDURE [dbo].[VerifiedOrganizationDomainSsoDetails_ReadByEmail] +@Email NVARCHAR(256) +AS +BEGIN + SET NOCOUNT ON + + DECLARE @Domain NVARCHAR(256) + + SELECT @Domain = SUBSTRING(@Email, CHARINDEX( '@', @Email) + 1, LEN(@Email)) + + SELECT + O.Id AS OrganizationId, + O.Name AS OrganizationName, + O.Identifier AS OrganizationIdentifier, + OD.DomainName + FROM [dbo].[OrganizationView] O + INNER JOIN [dbo].[OrganizationDomainView] OD ON O.Id = OD.OrganizationId + LEFT JOIN [dbo].[PolicyView] P ON O.Id = P.OrganizationId + LEFT JOIN [dbo].[Ssoconfig] S ON O.Id = S.OrganizationId + WHERE OD.DomainName = @Domain + AND O.Enabled = 1 + AND OD.VerifiedDate IS NOT NULL + AND P.Id IS NOT NULL AND P.[Type] = 4 -- SSO Type +END From d95eab70f4daf7c670da798521044611f3f023ab Mon Sep 17 00:00:00 2001 From: jrmccannon Date: Fri, 4 Oct 2024 09:57:50 -0500 Subject: [PATCH 11/12] Fixing stored proc to only use SSO config instead of the policy. --- .../VerfiedOrganaizationDomainSsoDetails_ReadByEmail.sql | 3 +-- ..._00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Sql/dbo/Stored Procedures/VerfiedOrganaizationDomainSsoDetails_ReadByEmail.sql b/src/Sql/dbo/Stored Procedures/VerfiedOrganaizationDomainSsoDetails_ReadByEmail.sql index ca6991d809a3..a32b42f6c1a1 100644 --- a/src/Sql/dbo/Stored Procedures/VerfiedOrganaizationDomainSsoDetails_ReadByEmail.sql +++ b/src/Sql/dbo/Stored Procedures/VerfiedOrganaizationDomainSsoDetails_ReadByEmail.sql @@ -15,10 +15,9 @@ BEGIN OD.DomainName FROM [dbo].[OrganizationView] O INNER JOIN [dbo].[OrganizationDomainView] OD ON O.Id = OD.OrganizationId - LEFT JOIN [dbo].[PolicyView] P ON O.Id = P.OrganizationId LEFT JOIN [dbo].[Ssoconfig] S ON O.Id = S.OrganizationId WHERE OD.DomainName = @Domain AND O.Enabled = 1 AND OD.VerifiedDate IS NOT NULL - AND P.Id IS NOT NULL AND P.[Type] = 4 -- SSO Type + AND S.Enabled = 1 END diff --git a/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql b/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql index 910ff0e4f6ae..e36ea1f46b3f 100644 --- a/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql +++ b/util/Migrator/DbScripts/2024-09-26_00_AddVerifiedOrganizationDomainSsoDetails_ReadByEmail.sql @@ -15,11 +15,10 @@ SELECT OD.DomainName FROM [dbo].[OrganizationView] O INNER JOIN [dbo].[OrganizationDomainView] OD ON O.Id = OD.OrganizationId - LEFT JOIN [dbo].[PolicyView] P ON O.Id = P.OrganizationId LEFT JOIN [dbo].[Ssoconfig] S ON O.Id = S.OrganizationId WHERE OD.DomainName = @Domain AND O.Enabled = 1 AND OD.VerifiedDate IS NOT NULL - AND P.Id IS NOT NULL AND P.[Type] = 4 -- SSO Type + AND S.Enabled = 1 END GO From b15e0a6365ee87f94fe78b23189052b96b184eb3 Mon Sep 17 00:00:00 2001 From: jrmccannon Date: Fri, 4 Oct 2024 14:31:56 -0500 Subject: [PATCH 12/12] Forgot to add org name to response. --- .../VerifiedOrganizationDomainSsoDetailResponseModel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailResponseModel.cs index d8516d31989c..be4d8865d8c1 100644 --- a/src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Organizations/VerifiedOrganizationDomainSsoDetailResponseModel.cs @@ -15,7 +15,9 @@ public VerifiedOrganizationDomainSsoDetailResponseModel(VerifiedOrganizationDoma DomainName = data.DomainName; OrganizationIdentifier = data.OrganizationIdentifier; + OrganizationName = data.OrganizationName; } public string DomainName { get; } public string OrganizationIdentifier { get; } + public string OrganizationName { get; } }