From 795034cd9dd85fed00ffde9c8262a9470fc23241 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Tue, 15 Aug 2023 15:10:56 +0200 Subject: [PATCH 01/10] [PM-3430] Refactor organization public key response (#3195) --- .../Controllers/OrganizationsController.cs | 13 ++++++++++--- .../OrganizationPublicKeyResponseModel.cs | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 src/Api/Models/Response/Organizations/OrganizationPublicKeyResponseModel.cs diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs index 1d0e9d148789..e1301e3f2513 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/Controllers/OrganizationsController.cs @@ -682,8 +682,8 @@ public async Task PutTaxInfo(string id, [FromBody] OrganizationTaxInfoUpdateRequ await _paymentService.SaveTaxInfoAsync(organization, taxInfo); } - [HttpGet("{id}/keys")] - public async Task GetKeys(string id) + [HttpGet("{id}/public-key")] + public async Task GetPublicKey(string id) { var org = await _organizationRepository.GetByIdAsync(new Guid(id)); if (org == null) @@ -691,7 +691,14 @@ public async Task GetKeys(string id) throw new NotFoundException(); } - return new OrganizationKeysResponseModel(org); + return new OrganizationPublicKeyResponseModel(org); + } + + [Obsolete("TDL-136 Renamed to public-key (2023.8), left for backwards compatability with older clients.")] + [HttpGet("{id}/keys")] + public async Task GetKeys(string id) + { + return await GetPublicKey(id); } [HttpPost("{id}/keys")] diff --git a/src/Api/Models/Response/Organizations/OrganizationPublicKeyResponseModel.cs b/src/Api/Models/Response/Organizations/OrganizationPublicKeyResponseModel.cs new file mode 100644 index 000000000000..0451433e1ba2 --- /dev/null +++ b/src/Api/Models/Response/Organizations/OrganizationPublicKeyResponseModel.cs @@ -0,0 +1,19 @@ +using Bit.Core.Entities; +using Bit.Core.Models.Api; + +namespace Bit.Api.Models.Response.Organizations; + +public class OrganizationPublicKeyResponseModel : ResponseModel +{ + public OrganizationPublicKeyResponseModel(Organization org) : base("organizationPublicKey") + { + if (org == null) + { + throw new ArgumentNullException(nameof(org)); + } + + PublicKey = org.PublicKey; + } + + public string PublicKey { get; set; } +} From c7b3759d551a7a538eb3707c3e58a048e4c331f4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 15:52:02 -0400 Subject: [PATCH 02/10] Bumped version to 2023.8.0 (#3201) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 1171da325d6f..61d7d5139ad7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ net6.0 - 2023.7.2 + 2023.8.0 Bit.$(MSBuildProjectName) true enable From 4ec01b0ef079a4be0d99198b2d620d94e04dc6f3 Mon Sep 17 00:00:00 2001 From: Ike <137194738+ike-kottlowski@users.noreply.github.com> Date: Tue, 15 Aug 2023 14:16:02 -0700 Subject: [PATCH 03/10] PM-2427-Defect-Cannot-delete-account-if-User-has-auth-request-on-table-FK-constraint (#3118) * updated migrations and ef repo * removed route alias --------- Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> --- src/Identity/Controllers/SsoController.cs | 2 - .../Repositories/UserRepository.cs | 1 + .../dbo/Stored Procedures/User_DeleteById.sql | 17 ++- .../2023-07-17_00_DeleteUserSproc.sql | 136 ++++++++++++++++++ 4 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 util/Migrator/DbScripts/2023-07-17_00_DeleteUserSproc.sql diff --git a/src/Identity/Controllers/SsoController.cs b/src/Identity/Controllers/SsoController.cs index f3a9afb17ce8..58a268291440 100644 --- a/src/Identity/Controllers/SsoController.cs +++ b/src/Identity/Controllers/SsoController.cs @@ -14,8 +14,6 @@ namespace Bit.Identity.Controllers; -// TODO: 2022-01-12, Remove account alias -[Route("account/[action]")] [Route("sso/[action]")] public class SsoController : Controller { diff --git a/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs b/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs index c48b8a582bb3..ba422c789b7c 100644 --- a/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs @@ -155,6 +155,7 @@ public override async Task DeleteAsync(Core.Entities.User user) dbContext.Ciphers.RemoveRange(dbContext.Ciphers.Where(c => c.UserId == user.Id)); dbContext.Folders.RemoveRange(dbContext.Folders.Where(f => f.UserId == user.Id)); + dbContext.AuthRequests.RemoveRange(dbContext.AuthRequests.Where(s => s.UserId == user.Id)); dbContext.Devices.RemoveRange(dbContext.Devices.Where(d => d.UserId == user.Id)); var collectionUsers = from cu in dbContext.CollectionUsers join ou in dbContext.OrganizationUsers on cu.OrganizationUserId equals ou.Id diff --git a/src/Sql/dbo/Stored Procedures/User_DeleteById.sql b/src/Sql/dbo/Stored Procedures/User_DeleteById.sql index ce32f2f24a42..1f16c15aaafa 100644 --- a/src/Sql/dbo/Stored Procedures/User_DeleteById.sql +++ b/src/Sql/dbo/Stored Procedures/User_DeleteById.sql @@ -31,6 +31,13 @@ BEGIN WHERE [UserId] = @Id + -- Delete AuthRequest, must be before Device + DELETE + FROM + [dbo].[AuthRequest] + WHERE + [UserId] = @Id + -- Delete devices DELETE FROM @@ -43,7 +50,7 @@ BEGIN CU FROM [dbo].[CollectionUser] CU - INNER JOIN + INNER JOIN [dbo].[OrganizationUser] OU ON OU.[Id] = CU.[OrganizationUserId] WHERE OU.[UserId] = @Id @@ -53,7 +60,7 @@ BEGIN GU FROM [dbo].[GroupUser] GU - INNER JOIN + INNER JOIN [dbo].[OrganizationUser] OU ON OU.[Id] = GU.[OrganizationUserId] WHERE OU.[UserId] = @Id @@ -63,7 +70,7 @@ BEGIN AP FROM [dbo].[AccessPolicy] AP - INNER JOIN + INNER JOIN [dbo].[OrganizationUser] OU ON OU.[Id] = AP.[OrganizationUserId] WHERE [UserId] = @Id @@ -95,7 +102,7 @@ BEGIN [dbo].[EmergencyAccess] WHERE [GrantorId] = @Id - OR + OR [GranteeId] = @Id -- Delete Sends @@ -104,7 +111,7 @@ BEGIN [dbo].[Send] WHERE [UserId] = @Id - + -- Finally, delete the user DELETE FROM diff --git a/util/Migrator/DbScripts/2023-07-17_00_DeleteUserSproc.sql b/util/Migrator/DbScripts/2023-07-17_00_DeleteUserSproc.sql new file mode 100644 index 000000000000..cbfb972901e5 --- /dev/null +++ b/util/Migrator/DbScripts/2023-07-17_00_DeleteUserSproc.sql @@ -0,0 +1,136 @@ +IF OBJECT_ID('[dbo].[User_DeleteById]') IS NOT NULL + BEGIN + DROP PROCEDURE [dbo].[User_DeleteById] +END +GO + +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO + +CREATE PROCEDURE [dbo].[User_DeleteById] + @Id UNIQUEIDENTIFIER +WITH + RECOMPILE +AS +BEGIN + SET NOCOUNT ON + DECLARE @BatchSize INT = 100 + + -- Delete ciphers + WHILE @BatchSize > 0 + BEGIN + BEGIN TRANSACTION User_DeleteById_Ciphers + + DELETE TOP(@BatchSize) + FROM + [dbo].[Cipher] + WHERE + [UserId] = @Id + + SET @BatchSize = @@ROWCOUNT + + COMMIT TRANSACTION User_DeleteById_Ciphers + END + + BEGIN TRANSACTION User_DeleteById + + -- Delete folders + DELETE + FROM + [dbo].[Folder] + WHERE + [UserId] = @Id + + -- Delete AuthRequest, must be before Device + DELETE + FROM + [dbo].[AuthRequest] + WHERE + [UserId] = @Id + + -- Delete devices + DELETE + FROM + [dbo].[Device] + WHERE + [UserId] = @Id + + -- Delete collection users + DELETE + CU + FROM + [dbo].[CollectionUser] CU + INNER JOIN + [dbo].[OrganizationUser] OU ON OU.[Id] = CU.[OrganizationUserId] + WHERE + OU.[UserId] = @Id + + -- Delete group users + DELETE + GU + FROM + [dbo].[GroupUser] GU + INNER JOIN + [dbo].[OrganizationUser] OU ON OU.[Id] = GU.[OrganizationUserId] + WHERE + OU.[UserId] = @Id + + -- Delete AccessPolicy + DELETE + AP + FROM + [dbo].[AccessPolicy] AP + INNER JOIN + [dbo].[OrganizationUser] OU ON OU.[Id] = AP.[OrganizationUserId] + WHERE + [UserId] = @Id + + -- Delete organization users + DELETE + FROM + [dbo].[OrganizationUser] + WHERE + [UserId] = @Id + + -- Delete provider users + DELETE + FROM + [dbo].[ProviderUser] + WHERE + [UserId] = @Id + + -- Delete SSO Users + DELETE + FROM + [dbo].[SsoUser] + WHERE + [UserId] = @Id + + -- Delete Emergency Accesses + DELETE + FROM + [dbo].[EmergencyAccess] + WHERE + [GrantorId] = @Id + OR + [GranteeId] = @Id + + -- Delete Sends + DELETE + FROM + [dbo].[Send] + WHERE + [UserId] = @Id + + -- Finally, delete the user + DELETE + FROM + [dbo].[User] + WHERE + [Id] = @Id + + COMMIT TRANSACTION User_DeleteById +END + From 60eab8a28edf164e1ecd0b414b700420b6a54e58 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Wed, 16 Aug 2023 09:03:17 +1000 Subject: [PATCH 04/10] [AC-1577] [AC-1575] Prefill Bitwarden portal values etc (#3155) * [AC-1575] Don't overwrite existing org information when changing plans * [AC-1577] Prefill SM configuration section when starting trial --- .../Controllers/OrganizationsController.cs | 9 ++ src/Admin/Models/OrganizationEditModel.cs | 19 +++- src/Admin/Models/OrganizationViewModel.cs | 18 +-- src/Admin/Views/Organizations/Edit.cshtml | 30 ++++- .../Organizations/_ViewInformation.cshtml | 8 +- .../Views/Providers/CreateOrganization.cshtml | 4 +- .../Views/Shared/_OrganizationForm.cshtml | 12 +- .../Shared/_OrganizationFormScripts.cshtml | 106 +++++++++++------- 8 files changed, 135 insertions(+), 71 deletions(-) diff --git a/src/Admin/Controllers/OrganizationsController.cs b/src/Admin/Controllers/OrganizationsController.cs index 2eb223716d5d..44b83c2f3449 100644 --- a/src/Admin/Controllers/OrganizationsController.cs +++ b/src/Admin/Controllers/OrganizationsController.cs @@ -5,6 +5,7 @@ using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; +using Bit.Core.Exceptions; using Bit.Core.Models.OrganizationConnectionConfigs; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces; using Bit.Core.Repositories; @@ -199,6 +200,14 @@ public async Task Edit(Guid id, OrganizationEditModel model) { var organization = await GetOrganization(id, model); + if (organization.UseSecretsManager && + !organization.SecretsManagerBeta + && StaticStore.GetSecretsManagerPlan(organization.PlanType) == null + ) + { + throw new BadRequestException("Plan does not support Secrets Manager"); + } + await _organizationRepository.ReplaceAsync(organization); await _applicationCacheService.UpsertOrganizationAbilityAsync(organization); await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.OrganizationEditedByAdmin, organization, _currentContext) diff --git a/src/Admin/Models/OrganizationEditModel.cs b/src/Admin/Models/OrganizationEditModel.cs index 7481ff6779d1..76222fbb18ba 100644 --- a/src/Admin/Models/OrganizationEditModel.cs +++ b/src/Admin/Models/OrganizationEditModel.cs @@ -28,9 +28,9 @@ public OrganizationEditModel(Provider provider) public OrganizationEditModel(Organization org, Provider provider, IEnumerable orgUsers, IEnumerable ciphers, IEnumerable collections, IEnumerable groups, IEnumerable policies, BillingInfo billingInfo, IEnumerable connections, - GlobalSettings globalSettings, int secrets, int projects, int serviceAccounts, int smSeats) + GlobalSettings globalSettings, int secrets, int projects, int serviceAccounts, int occupiedSmSeats) : base(org, provider, connections, orgUsers, ciphers, collections, groups, policies, secrets, projects, - serviceAccounts, smSeats) + serviceAccounts, occupiedSmSeats) { BillingInfo = billingInfo; BraintreeMerchantId = globalSettings.Braintree.MerchantId; @@ -145,13 +145,26 @@ public OrganizationEditModel(Organization org, Provider provider, IEnumerable> GetPlansHelper() => + StaticStore.SecretManagerPlans.Select(p => + new Dictionary + { + { "type", p.Type }, + { "baseServiceAccount", p.BaseServiceAccount } + }); + public Organization CreateOrganization(Provider provider) { BillingEmail = provider.BillingEmail; diff --git a/src/Admin/Models/OrganizationViewModel.cs b/src/Admin/Models/OrganizationViewModel.cs index 491ff9191bed..7320b8594d7c 100644 --- a/src/Admin/Models/OrganizationViewModel.cs +++ b/src/Admin/Models/OrganizationViewModel.cs @@ -13,7 +13,7 @@ public OrganizationViewModel() { } public OrganizationViewModel(Organization org, Provider provider, IEnumerable connections, IEnumerable orgUsers, IEnumerable ciphers, IEnumerable collections, IEnumerable groups, IEnumerable policies, int secretsCount, int projectCount, int serviceAccountsCount, - int smSeatsCount) + int occupiedSmSeatsCount) { Organization = org; @@ -39,10 +39,10 @@ public OrganizationViewModel(Organization org, Provider provider, IEnumerable u.Type == OrganizationUserType.Admin && u.Status == organizationUserStatus) .Select(u => u.Email)); - Secrets = secretsCount; - Projects = projectCount; - ServiceAccounts = serviceAccountsCount; - SmSeats = smSeatsCount; + SecretsCount = secretsCount; + ProjectsCount = projectCount; + ServiceAccountsCount = serviceAccountsCount; + OccupiedSmSeatsCount = occupiedSmSeatsCount; } public Organization Organization { get; set; } @@ -59,9 +59,9 @@ public OrganizationViewModel(Organization org, Provider provider, IEnumerable Organization.UseSecretsManager; } diff --git a/src/Admin/Views/Organizations/Edit.cshtml b/src/Admin/Views/Organizations/Edit.cshtml index e4c329007c4b..e3f6d50905b2 100644 --- a/src/Admin/Views/Organizations/Edit.cshtml +++ b/src/Admin/Views/Organizations/Edit.cshtml @@ -12,25 +12,45 @@ @section Scripts { @await Html.PartialAsync("_OrganizationFormScripts") - + } diff --git a/src/Admin/Views/Organizations/_ViewInformation.cshtml b/src/Admin/Views/Organizations/_ViewInformation.cshtml index df4513ccaa86..19432cd4f775 100644 --- a/src/Admin/Views/Organizations/_ViewInformation.cshtml +++ b/src/Admin/Views/Organizations/_ViewInformation.cshtml @@ -33,16 +33,16 @@
@Model.CollectionCount
Secrets
-
@(Model.UseSecretsManager ? Model.Secrets: "N/A")
+
@(Model.UseSecretsManager ? Model.SecretsCount: "N/A")
Projects
-
@(Model.UseSecretsManager ? Model.Projects: "N/A")
+
@(Model.UseSecretsManager ? Model.ProjectsCount: "N/A")
Service Accounts
-
@(Model.UseSecretsManager ? Model.ServiceAccounts: "N/A")
+
@(Model.UseSecretsManager ? Model.ServiceAccountsCount: "N/A")
Secrets Manager Seats
-
@(Model.UseSecretsManager ? Model.SmSeats: "N/A" )
+
@(Model.UseSecretsManager ? Model.OccupiedSmSeatsCount: "N/A" )
Groups
@Model.GroupCount
diff --git a/src/Admin/Views/Providers/CreateOrganization.cshtml b/src/Admin/Views/Providers/CreateOrganization.cshtml index 2257f6e33489..e4298960a007 100644 --- a/src/Admin/Views/Providers/CreateOrganization.cshtml +++ b/src/Admin/Views/Providers/CreateOrganization.cshtml @@ -5,10 +5,10 @@ @section Scripts { @await Html.PartialAsync("_OrganizationFormScripts") - + } diff --git a/src/Admin/Views/Shared/_OrganizationForm.cshtml b/src/Admin/Views/Shared/_OrganizationForm.cshtml index 3e816c9d761a..9b76d1c2a80c 100644 --- a/src/Admin/Views/Shared/_OrganizationForm.cshtml +++ b/src/Admin/Views/Shared/_OrganizationForm.cshtml @@ -83,7 +83,7 @@ var planTypes = Enum.GetValues() .Where(p => Model.Provider == null || p is >= PlanType.TeamsMonthly and <= PlanType.EnterpriseAnnually) .Select(e => new SelectListItem - { + { Value = ((int)e).ToString(), Text = e.GetDisplayAttribute()?.GetName() ?? e.ToString() }) @@ -176,7 +176,7 @@ } - + @if (canViewPlan) {

Password Manager Configuration

@@ -212,7 +212,7 @@ @if (canViewPlan) { -