From 624df4969860d34c8483051121e787b5821e6817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Mon, 13 Feb 2023 10:26:58 +0000 Subject: [PATCH] [EC-430] Admin portal: Update organization information screen (#2672) * [EC-430] Added ProviderOrganizationProviderDetailsView to get Provider details for an Organization * [EC-430] Added Provider information to Organization Edit/View on Admin panel * [EC-430] Remove "Add to Reseller" button * [EC-430] Removed unused property OrganizationEditModel.ClientOwnerEmail * [EC-430] Replaced IProviderOrganizationRepository.GetProviderDetailsByOrganizationAsync with IProviderRepository.GetByOrganizationIdAsync * [EC-430] Deleted ProviderOrganizationProviderDetails and ProviderOrganizationProviderDetailsReadByOrganizationIdQuery --- src/Admin/Controllers/OrganizationsController.cs | 9 +++++++-- src/Admin/Models/OrganizationEditModel.cs | 7 ++++--- src/Admin/Models/OrganizationViewModel.cs | 5 ++++- src/Admin/Views/Organizations/Edit.cshtml | 14 +++++++++++++- src/Admin/Views/Organizations/View.cshtml | 5 +++++ .../Organizations/_ProviderInformation.cshtml | 9 +++++++++ src/Core/Repositories/IProviderRepository.cs | 1 + .../Repositories/ProviderRepository.cs | 13 +++++++++++++ .../Repositories/ProviderRepository.cs | 14 ++++++++++++++ src/Sql/Sql.sqlproj | 1 + .../Provider_ReadByOrganizationId.sql | 15 +++++++++++++++ ...023-02-06_00_ProviderReadByOrganizationId.sql | 16 ++++++++++++++++ 12 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 src/Admin/Views/Organizations/_ProviderInformation.cshtml create mode 100644 src/Sql/dbo/Stored Procedures/Provider_ReadByOrganizationId.sql create mode 100644 util/Migrator/DbScripts/2023-02-06_00_ProviderReadByOrganizationId.sql diff --git a/src/Admin/Controllers/OrganizationsController.cs b/src/Admin/Controllers/OrganizationsController.cs index 76c00d025b..b3f77b697b 100644 --- a/src/Admin/Controllers/OrganizationsController.cs +++ b/src/Admin/Controllers/OrganizationsController.cs @@ -30,6 +30,7 @@ public class OrganizationsController : Controller private readonly GlobalSettings _globalSettings; private readonly IReferenceEventService _referenceEventService; private readonly IUserService _userService; + private readonly IProviderRepository _providerRepository; private readonly ILogger _logger; public OrganizationsController( @@ -47,6 +48,7 @@ public class OrganizationsController : Controller GlobalSettings globalSettings, IReferenceEventService referenceEventService, IUserService userService, + IProviderRepository providerRepository, ILogger logger) { _organizationRepository = organizationRepository; @@ -63,6 +65,7 @@ public class OrganizationsController : Controller _globalSettings = globalSettings; _referenceEventService = referenceEventService; _userService = userService; + _providerRepository = providerRepository; _logger = logger; } @@ -102,6 +105,7 @@ public class OrganizationsController : Controller return RedirectToAction("Index"); } + var provider = await _providerRepository.GetByOrganizationIdAsync(id); var ciphers = await _cipherRepository.GetManyByOrganizationIdAsync(id); var collections = await _collectionRepository.GetManyByOrganizationIdAsync(id); IEnumerable groups = null; @@ -116,7 +120,7 @@ public class OrganizationsController : Controller } var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id); var billingSyncConnection = _globalSettings.EnableCloudCommunication ? await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync) : null; - return View(new OrganizationViewModel(organization, billingSyncConnection, users, ciphers, collections, groups, policies)); + return View(new OrganizationViewModel(organization, provider, billingSyncConnection, users, ciphers, collections, groups, policies)); } [SelfHosted(NotSelfHostedOnly = true)] @@ -128,6 +132,7 @@ public class OrganizationsController : Controller return RedirectToAction("Index"); } + var provider = await _providerRepository.GetByOrganizationIdAsync(id); var ciphers = await _cipherRepository.GetManyByOrganizationIdAsync(id); var collections = await _collectionRepository.GetManyByOrganizationIdAsync(id); IEnumerable groups = null; @@ -143,7 +148,7 @@ public class OrganizationsController : Controller var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id); var billingInfo = await _paymentService.GetBillingAsync(organization); var billingSyncConnection = _globalSettings.EnableCloudCommunication ? await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync) : null; - return View(new OrganizationEditModel(organization, users, ciphers, collections, groups, policies, + return View(new OrganizationEditModel(organization, provider, users, ciphers, collections, groups, policies, billingInfo, billingSyncConnection, _globalSettings)); } diff --git a/src/Admin/Models/OrganizationEditModel.cs b/src/Admin/Models/OrganizationEditModel.cs index 2eeafb77a2..ab37ebe866 100644 --- a/src/Admin/Models/OrganizationEditModel.cs +++ b/src/Admin/Models/OrganizationEditModel.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using Bit.Core.Entities; +using Bit.Core.Entities.Provider; using Bit.Core.Enums; using Bit.Core.Models.Business; using Bit.Core.Models.Data.Organizations.OrganizationUsers; @@ -12,11 +13,11 @@ public class OrganizationEditModel : OrganizationViewModel { public OrganizationEditModel() { } - public OrganizationEditModel(Organization org, IEnumerable orgUsers, + public OrganizationEditModel(Organization org, Provider provider, IEnumerable orgUsers, IEnumerable ciphers, IEnumerable collections, IEnumerable groups, IEnumerable policies, BillingInfo billingInfo, IEnumerable connections, GlobalSettings globalSettings) - : base(org, connections, orgUsers, ciphers, collections, groups, policies) + : base(org, provider, connections, orgUsers, ciphers, collections, groups, policies) { BillingInfo = billingInfo; BraintreeMerchantId = globalSettings.Braintree.MerchantId; @@ -59,7 +60,7 @@ public class OrganizationEditModel : OrganizationViewModel public string BraintreeMerchantId { get; set; } [Required] - [Display(Name = "Name")] + [Display(Name = "Organization Name")] public string Name { get; set; } [Display(Name = "Business Name")] public string BusinessName { get; set; } diff --git a/src/Admin/Models/OrganizationViewModel.cs b/src/Admin/Models/OrganizationViewModel.cs index 31504f9371..363851bb49 100644 --- a/src/Admin/Models/OrganizationViewModel.cs +++ b/src/Admin/Models/OrganizationViewModel.cs @@ -1,4 +1,5 @@ using Bit.Core.Entities; +using Bit.Core.Entities.Provider; using Bit.Core.Enums; using Bit.Core.Models.Data.Organizations.OrganizationUsers; @@ -8,11 +9,12 @@ public class OrganizationViewModel { public OrganizationViewModel() { } - public OrganizationViewModel(Organization org, IEnumerable connections, + public OrganizationViewModel(Organization org, Provider provider, IEnumerable connections, IEnumerable orgUsers, IEnumerable ciphers, IEnumerable collections, IEnumerable groups, IEnumerable policies) { Organization = org; + Provider = provider; Connections = connections ?? Enumerable.Empty(); HasPublicPrivateKeys = org.PublicKey != null && org.PrivateKey != null; UserInvitedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Invited); @@ -34,6 +36,7 @@ public class OrganizationViewModel } public Organization Organization { get; set; } + public Provider Provider { get; set; } public IEnumerable Connections { get; set; } public string Owners { get; set; } public string Admins { get; set; } diff --git a/src/Admin/Views/Organizations/Edit.cshtml b/src/Admin/Views/Organizations/Edit.cshtml index b77be0d72e..c4726ed362 100644 --- a/src/Admin/Views/Organizations/Edit.cshtml +++ b/src/Admin/Views/Organizations/Edit.cshtml @@ -117,6 +117,11 @@

Organization @Model.Organization.Name

+@if (Model.Provider != null) +{ +

Provider Relationship

+ @await Html.PartialAsync("_ProviderInformation", Model.Provider) +}

Organization Information

@await Html.PartialAsync("_ViewInformation", Model)

Billing Information

@@ -283,7 +288,14 @@
- + @if (Model.Provider?.Type == ProviderType.Reseller) + { + + } + else + { + + }
diff --git a/src/Admin/Views/Organizations/View.cshtml b/src/Admin/Views/Organizations/View.cshtml index 38d58e61a0..77a76a021e 100644 --- a/src/Admin/Views/Organizations/View.cshtml +++ b/src/Admin/Views/Organizations/View.cshtml @@ -6,6 +6,11 @@

Organization @Model.Organization.Name

+@if (Model.Provider != null) +{ +

Provider Relationship

+ @await Html.PartialAsync("_ProviderInformation", Model.Provider) +}

Information

@await Html.PartialAsync("_ViewInformation", Model) @if(GlobalSettings.SelfHosted) diff --git a/src/Admin/Views/Organizations/_ProviderInformation.cshtml b/src/Admin/Views/Organizations/_ProviderInformation.cshtml new file mode 100644 index 0000000000..217f9951b0 --- /dev/null +++ b/src/Admin/Views/Organizations/_ProviderInformation.cshtml @@ -0,0 +1,9 @@ +@using Bit.SharedWeb.Utilities +@model Bit.Core.Entities.Provider.Provider +
+
Provider Name
+
@Model.Name
+ +
Provider Type
+
@(Model.Type.GetDisplayAttribute()?.GetName())
+
\ No newline at end of file diff --git a/src/Core/Repositories/IProviderRepository.cs b/src/Core/Repositories/IProviderRepository.cs index 8d92fb6d2e..8939bba966 100644 --- a/src/Core/Repositories/IProviderRepository.cs +++ b/src/Core/Repositories/IProviderRepository.cs @@ -5,6 +5,7 @@ namespace Bit.Core.Repositories; public interface IProviderRepository : IRepository { + Task GetByOrganizationIdAsync(Guid organizationId); Task> SearchAsync(string name, string userEmail, int skip, int take); Task> GetManyAbilitiesAsync(); } diff --git a/src/Infrastructure.Dapper/Repositories/ProviderRepository.cs b/src/Infrastructure.Dapper/Repositories/ProviderRepository.cs index 6a7f82b343..39bc029312 100644 --- a/src/Infrastructure.Dapper/Repositories/ProviderRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/ProviderRepository.cs @@ -18,6 +18,19 @@ public class ProviderRepository : Repository, IProviderRepositor : base(connectionString, readOnlyConnectionString) { } + public async Task GetByOrganizationIdAsync(Guid organizationId) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + "[dbo].[Provider_ReadByOrganizationId]", + new { OrganizationId = organizationId }, + commandType: CommandType.StoredProcedure); + + return results.FirstOrDefault(); + } + } + public async Task> SearchAsync(string name, string userEmail, int skip, int take) { using (var connection = new SqlConnection(ReadOnlyConnectionString)) diff --git a/src/Infrastructure.EntityFramework/Repositories/ProviderRepository.cs b/src/Infrastructure.EntityFramework/Repositories/ProviderRepository.cs index 62c4d79b84..8f66100738 100644 --- a/src/Infrastructure.EntityFramework/Repositories/ProviderRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/ProviderRepository.cs @@ -25,6 +25,20 @@ public class ProviderRepository : Repository, I await base.DeleteAsync(provider); } + public async Task GetByOrganizationIdAsync(Guid organizationId) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var query = from p in dbContext.Providers + join po in dbContext.ProviderOrganizations + on p.Id equals po.ProviderId + where po.OrganizationId == organizationId + select p; + return await query.FirstOrDefaultAsync(); + } + } + public async Task> SearchAsync(string name, string userEmail, int skip, int take) { using (var scope = ServiceScopeFactory.CreateScope()) diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj index 1ffe20cc11..57aa460852 100644 --- a/src/Sql/Sql.sqlproj +++ b/src/Sql/Sql.sqlproj @@ -282,6 +282,7 @@ + diff --git a/src/Sql/dbo/Stored Procedures/Provider_ReadByOrganizationId.sql b/src/Sql/dbo/Stored Procedures/Provider_ReadByOrganizationId.sql new file mode 100644 index 0000000000..9f775f9b27 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Provider_ReadByOrganizationId.sql @@ -0,0 +1,15 @@ +CREATE PROCEDURE [dbo].[Provider_ReadByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + P.* + FROM + [dbo].[ProviderView] P + INNER JOIN + [dbo].[ProviderOrganization] PO ON PO.[ProviderId] = P.[Id] + WHERE + PO.[OrganizationId] = @OrganizationId +END diff --git a/util/Migrator/DbScripts/2023-02-06_00_ProviderReadByOrganizationId.sql b/util/Migrator/DbScripts/2023-02-06_00_ProviderReadByOrganizationId.sql new file mode 100644 index 0000000000..9f8e5c42ca --- /dev/null +++ b/util/Migrator/DbScripts/2023-02-06_00_ProviderReadByOrganizationId.sql @@ -0,0 +1,16 @@ +CREATE OR ALTER PROCEDURE [dbo].[Provider_ReadByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + +SELECT + P.* +FROM + [dbo].[ProviderView] P +INNER JOIN + [dbo].[ProviderOrganization] PO ON PO.[ProviderId] = P.[Id] +WHERE + PO.[OrganizationId] = @OrganizationId +END +GO \ No newline at end of file