From 51e8d3f1a7260df0755090fb34a140369c7c8c71 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Fri, 7 Apr 2017 16:41:04 -0400 Subject: [PATCH] added max subvault count. check max org users. --- .../Api/Response/OrganizationResponseModel.cs | 2 +- src/Core/Models/StaticStore/Plan.cs | 1 + src/Core/Models/Table/Organization.cs | 2 +- .../IOrganizationUserRepository.cs | 1 + src/Core/Repositories/ISubvaultRepository.cs | 1 + .../SqlServer/OrganizationUserRepository.cs | 13 +++++++++++++ .../SqlServer/SubvaultRepository.cs | 13 +++++++++++++ .../Implementations/OrganizationService.cs | 19 ++++++++++++++++++- src/Core/Utilities/StaticStore.cs | 1 + src/Sql/Sql.sqlproj | 2 ++ ...nizationUser_ReadCountByOrganizationId.sql | 13 +++++++++++++ .../Subvault_ReadCountByOrganizationId.sql | 13 +++++++++++++ 12 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 src/Sql/dbo/Stored Procedures/OrganizationUser_ReadCountByOrganizationId.sql create mode 100644 src/Sql/dbo/Stored Procedures/Subvault_ReadCountByOrganizationId.sql diff --git a/src/Core/Models/Api/Response/OrganizationResponseModel.cs b/src/Core/Models/Api/Response/OrganizationResponseModel.cs index b491624ee8..9a548bae4e 100644 --- a/src/Core/Models/Api/Response/OrganizationResponseModel.cs +++ b/src/Core/Models/Api/Response/OrganizationResponseModel.cs @@ -32,7 +32,7 @@ namespace Bit.Core.Models.Api public string BillingEmail { get; set; } public string Plan { get; set; } public Enums.PlanType PlanType { get; set; } - public short MaxUsers { get; set; } + public short? MaxUsers { get; set; } } public class OrganizationBillingResponseModel : OrganizationResponseModel diff --git a/src/Core/Models/StaticStore/Plan.cs b/src/Core/Models/StaticStore/Plan.cs index a5241434c3..d70b58d948 100644 --- a/src/Core/Models/StaticStore/Plan.cs +++ b/src/Core/Models/StaticStore/Plan.cs @@ -18,6 +18,7 @@ namespace Bit.Core.Models.StaticStore public decimal UserMonthlyPrice { get; set; } public decimal BaseAnnualPrice { get; set; } public decimal UserAnnualPrice { get; set; } + public short? MaxSubvaults { get; set; } public bool Disabled { get; set; } } } diff --git a/src/Core/Models/Table/Organization.cs b/src/Core/Models/Table/Organization.cs index 4ad05e37b1..94c5f2bfbb 100644 --- a/src/Core/Models/Table/Organization.cs +++ b/src/Core/Models/Table/Organization.cs @@ -12,7 +12,7 @@ namespace Bit.Core.Models.Table public string BillingEmail { get; set; } public string Plan { get; set; } public PlanType PlanType { get; set; } - public short MaxUsers { get; set; } + public short? MaxUsers { get; set; } public short? MaxSubvaults { get; set; } public string StripeCustomerId { get; set; } public string StripeSubscriptionId { get; set; } diff --git a/src/Core/Repositories/IOrganizationUserRepository.cs b/src/Core/Repositories/IOrganizationUserRepository.cs index 6a7c31c7ac..13ea4c731d 100644 --- a/src/Core/Repositories/IOrganizationUserRepository.cs +++ b/src/Core/Repositories/IOrganizationUserRepository.cs @@ -9,6 +9,7 @@ namespace Bit.Core.Repositories { public interface IOrganizationUserRepository : IRepository { + Task GetCountByOrganizationIdAsync(Guid organizationId); Task GetCountByFreeOrganizationAdminUserAsync(Guid userId); Task> GetManyByUserAsync(Guid userId); Task> GetManyByOrganizationAsync(Guid organizationId, OrganizationUserType? type); diff --git a/src/Core/Repositories/ISubvaultRepository.cs b/src/Core/Repositories/ISubvaultRepository.cs index b74d9222e9..1297c3b4db 100644 --- a/src/Core/Repositories/ISubvaultRepository.cs +++ b/src/Core/Repositories/ISubvaultRepository.cs @@ -7,6 +7,7 @@ namespace Bit.Core.Repositories { public interface ISubvaultRepository : IRepository { + Task GetCountByOrganizationIdAsync(Guid organizationId); Task> GetManyByOrganizationIdAsync(Guid organizationId); Task> GetManyByUserIdAsync(Guid userId); diff --git a/src/Core/Repositories/SqlServer/OrganizationUserRepository.cs b/src/Core/Repositories/SqlServer/OrganizationUserRepository.cs index f6e792daca..5592e9684c 100644 --- a/src/Core/Repositories/SqlServer/OrganizationUserRepository.cs +++ b/src/Core/Repositories/SqlServer/OrganizationUserRepository.cs @@ -21,6 +21,19 @@ namespace Bit.Core.Repositories.SqlServer : base(connectionString) { } + public async Task GetCountByOrganizationIdAsync(Guid organizationId) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.ExecuteScalarAsync( + "[dbo].[OrganizationUser_ReadCountByOrganizationId]", + new { OrganizationId = organizationId }, + commandType: CommandType.StoredProcedure); + + return results; + } + } + public async Task GetCountByFreeOrganizationAdminUserAsync(Guid userId) { using(var connection = new SqlConnection(ConnectionString)) diff --git a/src/Core/Repositories/SqlServer/SubvaultRepository.cs b/src/Core/Repositories/SqlServer/SubvaultRepository.cs index b89ac86f7b..c265620bc0 100644 --- a/src/Core/Repositories/SqlServer/SubvaultRepository.cs +++ b/src/Core/Repositories/SqlServer/SubvaultRepository.cs @@ -19,6 +19,19 @@ namespace Bit.Core.Repositories.SqlServer : base(connectionString) { } + public async Task GetCountByOrganizationIdAsync(Guid organizationId) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.ExecuteScalarAsync( + "[dbo].[Subvault_ReadCountByOrganizationId]", + new { OrganizationId = organizationId }, + commandType: CommandType.StoredProcedure); + + return results; + } + } + public async Task> GetManyByOrganizationIdAsync(Guid organizationId) { using(var connection = new SqlConnection(ConnectionString)) diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 42814113b3..ce47358078 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -76,7 +76,7 @@ namespace Bit.Core.Services public async Task> SignUpAsync(OrganizationSignup signup) { - var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == signup.Plan); + var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == signup.Plan && !p.Disabled); if(plan == null) { throw new BadRequestException("Plan not found."); @@ -136,6 +136,7 @@ namespace Bit.Core.Services BusinessName = signup.BusinessName, PlanType = plan.Type, MaxUsers = (short)(plan.BaseUsers + (plan.CanBuyAdditionalUsers ? signup.AdditionalUsers : 0)), + MaxSubvaults = plan.MaxSubvaults, Plan = plan.ToString(), StripeCustomerId = customer?.Id, StripeSubscriptionId = subscription?.Id, @@ -184,6 +185,22 @@ namespace Bit.Core.Services public async Task InviteUserAsync(Guid organizationId, Guid invitingUserId, string email, Enums.OrganizationUserType type, IEnumerable subvaults) { + var organization = await _organizationRepository.GetByIdAsync(organizationId); + if(organization == null) + { + throw new NotFoundException(); + } + + if(organization.MaxUsers.HasValue) + { + var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organizationId); + if(userCount >= organization.MaxUsers.Value) + { + throw new BadRequestException("You have reached the maximum number of users " + + $"({organization.MaxUsers.Value}) for this organization."); + } + } + // Make sure user is not already invited var existingOrgUser = await _organizationUserRepository.GetByOrganizationAsync(organizationId, email); if(existingOrgUser != null) diff --git a/src/Core/Utilities/StaticStore.cs b/src/Core/Utilities/StaticStore.cs index daa267c27d..ef48888f93 100644 --- a/src/Core/Utilities/StaticStore.cs +++ b/src/Core/Utilities/StaticStore.cs @@ -96,6 +96,7 @@ namespace Bit.Core.Utilities Type = PlanType.Free, BaseUsers = 2, CanBuyAdditionalUsers = false, + MaxSubvaults = 2, Name = "Free" }, new Plan diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj index aeb902c973..5719e1dbc7 100644 --- a/src/Sql/Sql.sqlproj +++ b/src/Sql/Sql.sqlproj @@ -181,5 +181,7 @@ + + \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadCountByOrganizationId.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadCountByOrganizationId.sql new file mode 100644 index 0000000000..0ec3738545 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadCountByOrganizationId.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[OrganizationUser_ReadCountByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + COUNT(1) + FROM + [dbo].[OrganizationUser] + WHERE + [OrganizationId] = @OrganizationId +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Subvault_ReadCountByOrganizationId.sql b/src/Sql/dbo/Stored Procedures/Subvault_ReadCountByOrganizationId.sql new file mode 100644 index 0000000000..2e60048a6b --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Subvault_ReadCountByOrganizationId.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[Subvault_ReadCountByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + COUNT(1) + FROM + [dbo].[Subvault] + WHERE + [OrganizationId] = @OrganizationId +END \ No newline at end of file