From cf38ff3c1920f69ca5285d989b57379419ed710e Mon Sep 17 00:00:00 2001
From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com>
Date: Fri, 17 Nov 2023 15:12:49 -0500
Subject: [PATCH] [AC-1828] [AC-1807] Aggregated admin panel pricing fixes
(#3448)
* AC-1828: Allow reseller to add all teams and enterprise orgs
* AC-1807: Only show provider-eligible plans on Organization edit
* Thomas' feedback
* Matt's feedback
---
.../Views/Shared/_OrganizationForm.cshtml | 6 +-
.../Repositories/OrganizationRepository.cs | 15 +++--
...rganization_UnassignedToProviderSearch.sql | 4 +-
...OrganizationUnassignedToProviderSearch.sql | 64 +++++++++++++++++++
4 files changed, 81 insertions(+), 8 deletions(-)
create mode 100644 util/Migrator/DbScripts/2023-11-14_00_UpdateOrganizationUnassignedToProviderSearch.sql
diff --git a/src/Admin/Views/Shared/_OrganizationForm.cshtml b/src/Admin/Views/Shared/_OrganizationForm.cshtml
index 76502ff75..72076cbd3 100644
--- a/src/Admin/Views/Shared/_OrganizationForm.cshtml
+++ b/src/Admin/Views/Shared/_OrganizationForm.cshtml
@@ -82,7 +82,11 @@
@{
var planTypes = Enum.GetValues()
- .Where(p => Model.Provider == null || p is >= PlanType.TeamsMonthly and <= PlanType.TeamsStarter)
+ .Where(p =>
+ Model.Provider == null ||
+ (Model.Provider != null
+ && p is >= PlanType.TeamsMonthly2019 and <= PlanType.EnterpriseAnnually2019 or >= PlanType.TeamsMonthly2020 and <= PlanType.EnterpriseAnnually)
+ )
.Select(e => new SelectListItem
{
Value = ((int)e).ToString(),
diff --git a/src/Infrastructure.EntityFramework/Repositories/OrganizationRepository.cs b/src/Infrastructure.EntityFramework/Repositories/OrganizationRepository.cs
index b20026a6b..b7ee85543 100644
--- a/src/Infrastructure.EntityFramework/Repositories/OrganizationRepository.cs
+++ b/src/Infrastructure.EntityFramework/Repositories/OrganizationRepository.cs
@@ -96,12 +96,17 @@ public class OrganizationRepository : Repository> SearchUnassignedToProviderAsync(string name, string ownerEmail, int skip, int take)
{
using var scope = ServiceScopeFactory.CreateScope();
+
var dbContext = GetDatabaseContext(scope);
- var query = from o in dbContext.Organizations
- where o.PlanType >= PlanType.TeamsMonthly2020 && o.PlanType <= PlanType.EnterpriseAnnually &&
- !dbContext.ProviderOrganizations.Any(po => po.OrganizationId == o.Id) &&
- (string.IsNullOrWhiteSpace(name) || EF.Functions.Like(o.Name, $"%{name}%"))
- select o;
+
+ var query =
+ from o in dbContext.Organizations
+ where
+ ((o.PlanType >= PlanType.TeamsMonthly2019 && o.PlanType <= PlanType.EnterpriseAnnually2019) ||
+ (o.PlanType >= PlanType.TeamsMonthly2020 && o.PlanType <= PlanType.EnterpriseAnnually)) &&
+ !dbContext.ProviderOrganizations.Any(po => po.OrganizationId == o.Id) &&
+ (string.IsNullOrWhiteSpace(name) || EF.Functions.Like(o.Name, $"%{name}%"))
+ select o;
if (string.IsNullOrWhiteSpace(ownerEmail))
{
diff --git a/src/Sql/dbo/Stored Procedures/Organization_UnassignedToProviderSearch.sql b/src/Sql/dbo/Stored Procedures/Organization_UnassignedToProviderSearch.sql
index 45fcbaeb9..dc71c809e 100644
--- a/src/Sql/dbo/Stored Procedures/Organization_UnassignedToProviderSearch.sql
+++ b/src/Sql/dbo/Stored Procedures/Organization_UnassignedToProviderSearch.sql
@@ -21,7 +21,7 @@ BEGIN
INNER JOIN
[dbo].[User] U ON U.[Id] = OU.[UserId]
WHERE
- O.[PlanType] >= 8 AND O.[PlanType] <= 11 -- Get 'Team' and 'Enterprise' Organizations
+ ((O.[PlanType] >= 2 AND O.[PlanType] <= 5) OR (O.[PlanType] >= 8 AND O.[PlanType] <= 15)) -- All 'Teams' and 'Enterprise' organizations
AND NOT EXISTS (SELECT * FROM [dbo].[ProviderOrganizationView] PO WHERE PO.[OrganizationId] = O.[Id])
AND (@Name IS NULL OR O.[Name] LIKE @NameLikeSearch)
AND (U.[Email] LIKE @OwnerLikeSearch)
@@ -36,7 +36,7 @@ BEGIN
FROM
[dbo].[OrganizationView] O
WHERE
- O.[PlanType] >= 8 AND O.[PlanType] <= 11 -- Get 'Team' and 'Enterprise' Organizations
+ ((O.[PlanType] >= 2 AND O.[PlanType] <= 5) OR (O.[PlanType] >= 8 AND O.[PlanType] <= 15)) -- All 'Teams' and 'Enterprise' organizations
AND NOT EXISTS (SELECT * FROM [dbo].[ProviderOrganizationView] PO WHERE PO.[OrganizationId] = O.[Id])
AND (@Name IS NULL OR O.[Name] LIKE @NameLikeSearch)
ORDER BY O.[CreationDate] DESC
diff --git a/util/Migrator/DbScripts/2023-11-14_00_UpdateOrganizationUnassignedToProviderSearch.sql b/util/Migrator/DbScripts/2023-11-14_00_UpdateOrganizationUnassignedToProviderSearch.sql
new file mode 100644
index 000000000..5d85eb486
--- /dev/null
+++ b/util/Migrator/DbScripts/2023-11-14_00_UpdateOrganizationUnassignedToProviderSearch.sql
@@ -0,0 +1,64 @@
+/*
+ This procedure is used by the Bitwarden Admin Panel to retrieve the
+ Organizations a Reseller Provider is capable of adding as a client.
+
+ Currently, the procedure is only surfacing Organizations with the most
+ current Enterprise or Teams plans, but we actually need to surface any
+ Enterprise or Teams plan regardless of the version as all of them are
+ applicable to Resellers.
+*/
+
+-- Drop existing SPROC
+IF OBJECT_ID('[dbo].[Organization_UnassignedToProviderSearch]') IS NOT NULL
+BEGIN
+ DROP PROCEDURE [dbo].[Organization_UnassignedToProviderSearch]
+END
+GO
+
+CREATE PROCEDURE [dbo].[Organization_UnassignedToProviderSearch]
+ @Name NVARCHAR(50),
+ @OwnerEmail NVARCHAR(256),
+ @Skip INT = 0,
+ @Take INT = 25
+WITH RECOMPILE
+AS
+BEGIN
+ SET NOCOUNT ON
+ DECLARE @NameLikeSearch NVARCHAR(55) = '%' + @Name + '%'
+ DECLARE @OwnerLikeSearch NVARCHAR(55) = @OwnerEmail + '%'
+
+ IF @OwnerEmail IS NOT NULL
+ BEGIN
+ SELECT
+ O.*
+ FROM
+ [dbo].[OrganizationView] O
+ INNER JOIN
+ [dbo].[OrganizationUser] OU ON O.[Id] = OU.[OrganizationId]
+ INNER JOIN
+ [dbo].[User] U ON U.[Id] = OU.[UserId]
+ WHERE
+ ((O.[PlanType] >= 2 AND O.[PlanType] <= 5) OR (O.[PlanType] >= 8 AND O.[PlanType] <= 15)) -- All 'Teams' and 'Enterprise' organizations
+ AND NOT EXISTS (SELECT * FROM [dbo].[ProviderOrganizationView] PO WHERE PO.[OrganizationId] = O.[Id])
+ AND (@Name IS NULL OR O.[Name] LIKE @NameLikeSearch)
+ AND (U.[Email] LIKE @OwnerLikeSearch)
+ ORDER BY O.[CreationDate] DESC
+ OFFSET @Skip ROWS
+ FETCH NEXT @Take ROWS ONLY
+ END
+ ELSE
+ BEGIN
+ SELECT
+ O.*
+ FROM
+ [dbo].[OrganizationView] O
+ WHERE
+ ((O.[PlanType] >= 2 AND O.[PlanType] <= 5) OR (O.[PlanType] >= 8 AND O.[PlanType] <= 15)) -- All 'Teams' and 'Enterprise' organizations
+ AND NOT EXISTS (SELECT * FROM [dbo].[ProviderOrganizationView] PO WHERE PO.[OrganizationId] = O.[Id])
+ AND (@Name IS NULL OR O.[Name] LIKE @NameLikeSearch)
+ ORDER BY O.[CreationDate] DESC
+ OFFSET @Skip ROWS
+ FETCH NEXT @Take ROWS ONLY
+ END
+END
+GO