1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-05 18:12:48 -05:00

[AC-1081] Merge feature/billing-obfuscation (#2665)

* [EC-1014] Create Organization Status (Pending/Created) (#2610)

* [EC-427] Add columns 'Type' and 'BillingPhone' to Provider table

* [EC-427] Provider table Type and BillingPhone MySql migrations

* [EC-427] Provider table Type and BillingPhone Postgres migrations

* [EC-427] Add mysql migration script

* [EC-427] Add mysql migration script

* [EC-427] Updated Provider sql script to include default column value

* [EC-427] Removed default value from Provider.Type column

* [EC-427] Changed migration script to include a default value constraint instead of updating the null type

* [EC-427] Updated Sql project Provider table script

* [EC-427] Changed migration script to use 'Create OR Alter' for views and sprocs

* [EC-427] Added default values for 'BillingPhone' and 'Type' fields on sprocs [dbo].[Provider_Create] and [dbo].[Provider_Update]

* [EC-427] Adjusting metadata in migration script

* [EC-427] Updated Provider sprocs SQL script files

* [EC-427] Fixed migration script

* [EC-427] Added sqlite migration

* [EC-427] Add missing Provider_Update sproc default value

* [EC-427] Added missing GO action to migration script

* [EC-428] Redirect to Edit after creating Provider

* Revert "[EC-428] Redirect to Edit after creating Provider"

This reverts commit 6347bca1ed.

* [EC-1014] Create OrganizationStatusType and add Status column to Organizations table

* [EC-1014] Added EF migrations

* [EC-1014] dotnet format

* [EC-1014] Changed Organization.Status from SMALLINT to TINYINT

* [EC-1014] Set Organization.Status default value = 1

* [EC-1014] Setting Organization.Status default value as 1

* [EC-459 / EC-428] Admin panel: Add Provider Type to list and creation flow (#2593)

* [EC-427] Add columns 'Type' and 'BillingPhone' to Provider table

* [EC-427] Provider table Type and BillingPhone MySql migrations

* [EC-427] Provider table Type and BillingPhone Postgres migrations

* [EC-427] Add mysql migration script

* [EC-427] Add mysql migration script

* [EC-427] Updated Provider sql script to include default column value

* [EC-427] Removed default value from Provider.Type column

* [EC-427] Changed migration script to include a default value constraint instead of updating the null type

* [EC-427] Updated Sql project Provider table script

* [EC-427] Changed migration script to use 'Create OR Alter' for views and sprocs

* [EC-427] Added default values for 'BillingPhone' and 'Type' fields on sprocs [dbo].[Provider_Create] and [dbo].[Provider_Update]

* [EC-427] Adjusting metadata in migration script

* [EC-427] Updated Provider sprocs SQL script files

* [EC-427] Fixed migration script

* [EC-427] Added sqlite migration

* [EC-427] Add missing Provider_Update sproc default value

* [EC-427] Added missing GO action to migration script

* [EC-459] Added Type column to Providers list

* [EC-428] Added Type, BusinessName and BillingEmail to CreateProviderModel

* [EC-428] Updated Create Provider view to include new fields

* [EC-428] Updated ProviderService to not create a ProviderUser for the type Reseller

* [EC-428] Added custom validation for Provider fields depending on selected Type

* [EC-428] Redirect to Edit after creating Provider

* [EC-428] Setting Provider status as Created for Resellers

* [EC-428] Redirect on Provider creation depending if self host server

* [EC-428] Split ProviderService.CreateAsync into two methods: CreateMspAsync and CreateResellerAsync

* [EC-428] Created ICreateProviderCommand and added service for injection on Admin.Startup

* [EC-428] Modified Provider views to use DisplayName attribute values

* [EC-428] Moved ICreateProviderCommand to Core project

* [EC-428] Adding ICreateProviderCommand injection next to IProviderService

* [EC-428] Moved CreateProviderCommand to Commercial.Core project

* [EC-459] Added Type column to Providers list

* [EC-428] Added Type, BusinessName and BillingEmail to CreateProviderModel

* [EC-428] Updated Create Provider view to include new fields

* [EC-428] Updated ProviderService to not create a ProviderUser for the type Reseller

* [EC-428] Added custom validation for Provider fields depending on selected Type

* [EC-428] Redirect to Edit after creating Provider

* [EC-428] Setting Provider status as Created for Resellers

* [EC-428] Redirect on Provider creation depending if self host server

* [EC-428] Split ProviderService.CreateAsync into two methods: CreateMspAsync and CreateResellerAsync

* [EC-428] Created ICreateProviderCommand and added service for injection on Admin.Startup

* [EC-428] Modified Provider views to use DisplayName attribute values

* [EC-428] Moved ICreateProviderCommand to Core project

* [EC-428] Adding ICreateProviderCommand injection next to IProviderService

* [EC-428] Moved CreateProviderCommand to Commercial.Core project

* [EC-428] Moved CreateProviderCommand to namespace Bit.Commercial.Core.Providers

* [EC-429] Provider details screen updated with Type, BillingPhone and Organization details (#2666)

* [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

* [EC-429] Only show Create/Add Existing Organization buttons for Reseller providers (#2723)

* [EC-432] Add existing Organizations to Provider (#2683)

* [EC-432] Added ProviderOrganizationUnassignedOrganizationDetails_Search stored procedure

* [EC-432] Added IProviderOrganizationRepository.SearchAsync

* [EC-432] Created controller ProviderOrganizationsController to assign Organizations to a Provider

* [EC-432] Filter existing organizations by plans Enterprise or Team

* [EC-432] Existing Organization name links to edit page

* [EC-432] EF filtering out existing organizations by plan type enterprise or teams

* [EC-432] Creating multiple ProviderOrganization records

* [EC-432] Added ProviderOrganizationUnassignedOrganizationDetails_Search stored procedure

* [EC-432] Added IProviderOrganizationRepository.SearchAsync

* [EC-432] Created controller ProviderOrganizationsController to assign Organizations to a Provider

* [EC-432] Filter existing organizations by plans Enterprise or Team

* [EC-432] Existing Organization name links to edit page

* [EC-432] EF filtering out existing organizations by plan type enterprise or teams

* [EC-432] Creating multiple ProviderOrganization records

* [EC-432] Renamed migration script and added missing sproc

* [EC-432] Saving multiple events for the created ProviderOrganizations

* [EC-432] Included unit testing for ProviderService.AddOrganizations and EventService.LogProviderOrganizationEventsAsync

* [EC-432] Removed async from NoopEventService.LogProviderOrganizationEventsAsync

* [EC-432] Remove unused dependency setup in ProviderServiceTests.AddOrganizations_Success

* [EC-432] Renamed AddOrganizations to AddOrganizationsToReseller and removed addingUserId and key arguments

* [EC-432] Added DisplayName attributes to ProviderOrganizationViewModel and used them in the view

* [EC-432] Reverted changes to input fields

* [EC-432] Moved unassigned organizations search to Organizations repo

* [EC-432] Moved AddExistingOrganization action to ProvidersController

* [EC-432] dotnet format

* [EC-432] Fixed unit test issues

* [EC-432] Removed unnecessary Html.DisplayNameFor for labels

* [EC-432] Renamed OrganizationSearchViewModel to OrganizationUnassignedToProviderSearchViewModel

* [EC-432] Modified IEventService.LogProviderOrganizationEventsAsync to receive an IEnumerable as parameter

* [EC-432] Updated IProviderOrganizationRepository and replaced CreateWithManyOrganizations method with CreateManyAsync

* [EC-432] Deleted ProviderOrganization_CreateWithManyOrganizations

* [AC-432] Simplified Organization_UnassignedToProviderSearch query

* [AC-432] Removed unnecessary setup

* [EC-432] Checking if stored procedure exists before creating

* [EC-432] Renamed migration file to recent date

* [EC-435] Admin Portal: Add new Organization creation flow UI (#2707)

* [EC-435] Created _OrganizationForm partial view. Added actions for creating an Organization assigned to a provider

* [EC-435] Remove logic for creating an organization

* [EC-435] Created partial view _OrganizationFormScripts

* [EC-435] Remove unused ReferenceEventType

* [EC-435] Added TODO comment on Organization Create

* [EC-435] Checking if Provider type is Reseller on creating new assigned organization

* [EC-435] Setting the Organization plan type as TeamsMonthly by default when adding to a provider

* [EC-435] Removing unused buttons

* [EC-435] Switched hidden fields to form submit route value

* [EC-435] Moved _OrganizationForm and _OrganizationFormScripts to Shared folder

* [EC-435] Moved Create organization actions from OrganizationsController to ProvidersController

* [EC-435] Fixing bug on saving Organization that would have BillingEmail as null

* [EC-435] Added null check to Provider

* [EC-435] Moved trial buttons script logic to Edit view

* [AC-431] Add new organization invite process (#2737)

* [EC-435] Created _OrganizationForm partial view. Added actions for creating an Organization assigned to a provider

* [EC-435] Remove logic for creating an organization

* [EC-435] Created partial view _OrganizationFormScripts

* [EC-435] Remove unused ReferenceEventType

* [EC-435] Added TODO comment on Organization Create

* [EC-435] Checking if Provider type is Reseller on creating new assigned organization

* [EC-435] Setting the Organization plan type as TeamsMonthly by default when adding to a provider

* [EC-435] Removing unused buttons

* [EC-435] Switched hidden fields to form submit route value

* [EC-435] Moved _OrganizationForm and _OrganizationFormScripts to Shared folder

* [EC-435] Moved Create organization actions from OrganizationsController to ProvidersController

* [AC-431] Added new ReferenceEventType OrganizationCreatedByAdmin

* [AC-431] Added method IOrganizationService.CreateOrganization

* [AC-431] Creating new Organization with Pending status and assigning to Provider

* [AC-431] Added method to IMailService to send invitation to initialize org

* [AC-431] Added methods CreatePendingOrganization and InitPendingOrganization to IOrganizationService

* [AC-431] Org invite includes initOrganization parameter

* [AC-431] Modified existing Accept organization user action to initialize org

* [AC-431] Updated ProvidersController method name

* [AC-431] Created OrganizationUserInitInvitedViewModel to link to 'accept-init-organization' url

* [AC-431] Added action AcceptInit to OrganizationUsersController

* [AC-431] Resend owner invite

* [AC-431] dotnet format

* [AC-431] Removed unused parameter 'addingUserId' from IProviderService.AddOrganization

* [AC-431] Removed setting manual values for CreationDate and RevisionDate

* [AC-431] Updated OrganizationService.InitPendingOrganization to throw exceptions when the Organization does not meet the required criteria

* [AC-431] Modified OrganizationUserInitInvitedViewModel to inherit properties from OrganizationUserInvitedViewModel

* [AC-431] Removed unecessary parameter check

* [AC-431] Moved method description to IOrganizationService.InitPendingOrganization

* [AC-431] Moved ApplicationCacheService.UpsertOrganizationAbilityAsync and ReferenceEventService.RaiseEventAsync to OrganizationService

* [AC-431] Creating collection after creating organization

* [EC-435] Fixing bug on saving Organization that would have BillingEmail as null

* [AC-431] Deleted OrganizationUserInitInvitedViewModel and added parameter InitOrganization to OrganizationUserInvitedViewModel.cs

* [AC-431] Checking if the user has any existing SingleOrg policies before initializing an Org

* [AC-431] Remove commented code

* [EC-435] Added null check to Provider

* [EC-435] Moved trial buttons script logic to Edit view

* [AC-431] Added EncryptedString attribute to OrganizationUserAcceptInitRequestModel.CollectionName

* [AC-431] Refactored plan check condition

* [AC-431] Remove duplicate _applicationCacheService.UpsertOrganizationAbilityAsync call

* [AC-431] Removed IMailService.SendOrganizationInitInviteEmailAsync

* [AC-431] Added parameters ClaimsPrincipal and IUserService to IOrganizationService.CreatePendingOrganization

* [AC-434] Hide Billing screen for Reseller clients (#2783)

* [AC-434] Added ProviderType to ProfileOrganizationResponseModel

* [AC-434] Migration script

* [AC-434] Fixed indentation on migration script

* [AC-434] Hiding sensitive subscription data if the user does not have permissions

* [AC-434] Fixed missing dependency in unit test

* [AC-434] Altered BillingSubscription.Amount and BillingSubscriptionUpcomingInvoice.Amount to nullable

* [AC-434] Replaced CurrentContext.ManageBilling with ViewBillingHistory, ViewSubscription, EditSubscription and EditPaymentMethods

* [AC-434] Reverted change on BillingSubscription.Amount and now setting Subscription.Items = null when User does not have permission

* [AC-434] Added ProviderOrganizationProviderDetails_ReadByUserId

* [AC-434] Added IProviderOrganizationRepository.GetManyByUserAsync

* [AC-434] Added CurrentContext.GetOrganizationProviderDetails

* [AC-434] Remove unneeded join Organization table

* [AC-1255] Search Existing Organizations by partial Email (#2830)

* [AC-1255] Added email search field input validation

* [AC-1255] Reverted added email pattern

* [AC-1255] Modified Organization search by Email to search using substring

* [AC-1276] Displaying an Organizations pending owners if the Organization is in a Pending status (#2834)

* [AC-432] Checking that an existing Organization is not assigned to any Provider before being assigned (#2840)

* [AC-432] Checking if any of the selected Organizations is already assigned to a Provider

* [AC-432] Changed ProviderOrganization_ReadByOrganizationIds to only get count

* [AC-432] Replaced IProviderOrganizationRepository.GetCountByOrganizationIdsAsync with call to IProviderOrganizationRepository.GetByOrganizationId

* [AC-432] undo new line

* [AC-432] Fixed unit test

* Revert "[AC-432] Replaced IProviderOrganizationRepository.GetCountByOrganizationIdsAsync with call to IProviderOrganizationRepository.GetByOrganizationId"

This reverts commit ee6e095e88.

# Conflicts:
#	util/Migrator/DbScripts/2023-03-22_00_ProviderAddExistingOrganizations.sql

* [AC-432] Created new migration script for ProviderOrganization_ReadCountByOrganizationIds
This commit is contained in:
Rui Tomé
2023-04-14 11:13:16 +01:00
committed by GitHub
parent 03c740dffb
commit f5a8cf5c9c
96 changed files with 8689 additions and 431 deletions

View File

@ -17,6 +17,7 @@ namespace Bit.Admin.Controllers;
[Authorize]
public class OrganizationsController : Controller
{
private readonly IOrganizationService _organizationService;
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
@ -31,9 +32,11 @@ public class OrganizationsController : Controller
private readonly GlobalSettings _globalSettings;
private readonly IReferenceEventService _referenceEventService;
private readonly IUserService _userService;
private readonly IProviderRepository _providerRepository;
private readonly ILogger<OrganizationsController> _logger;
public OrganizationsController(
IOrganizationService organizationService,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IOrganizationConnectionRepository organizationConnectionRepository,
@ -48,8 +51,10 @@ public class OrganizationsController : Controller
GlobalSettings globalSettings,
IReferenceEventService referenceEventService,
IUserService userService,
IProviderRepository providerRepository,
ILogger<OrganizationsController> logger)
{
_organizationService = organizationService;
_organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository;
_organizationConnectionRepository = organizationConnectionRepository;
@ -64,6 +69,7 @@ public class OrganizationsController : Controller
_globalSettings = globalSettings;
_referenceEventService = referenceEventService;
_userService = userService;
_providerRepository = providerRepository;
_logger = logger;
}
@ -103,6 +109,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<Group> groups = null;
@ -117,7 +124,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)]
@ -129,6 +136,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<Group> groups = null;
@ -144,7 +152,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));
}
@ -214,4 +222,21 @@ public class OrganizationsController : Controller
return RedirectToAction("Index");
}
[HttpPost]
public async Task<IActionResult> ResendOwnerInvite(Guid id)
{
var organization = await _organizationRepository.GetByIdAsync(id);
if (organization == null)
{
return RedirectToAction("Index");
}
var organizationUsers = await _organizationUserRepository.GetManyByOrganizationAsync(id, OrganizationUserType.Owner);
foreach (var organizationUser in organizationUsers)
{
await _organizationService.ResendInviteAsync(id, null, organizationUser.Id, true);
}
return Json(null);
}
}

View File

@ -1,5 +1,7 @@
using Bit.Admin.Models;
using Bit.Core.Entities.Provider;
using Bit.Core.Enums.Provider;
using Bit.Core.Providers.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
@ -13,23 +15,42 @@ namespace Bit.Admin.Controllers;
[SelfHosted(NotSelfHostedOnly = true)]
public class ProvidersController : Controller
{
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationService _organizationService;
private readonly IProviderRepository _providerRepository;
private readonly IProviderUserRepository _providerUserRepository;
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
private readonly GlobalSettings _globalSettings;
private readonly IApplicationCacheService _applicationCacheService;
private readonly IProviderService _providerService;
private readonly IReferenceEventService _referenceEventService;
private readonly IUserService _userService;
private readonly ICreateProviderCommand _createProviderCommand;
public ProvidersController(IProviderRepository providerRepository, IProviderUserRepository providerUserRepository,
IProviderOrganizationRepository providerOrganizationRepository, IProviderService providerService,
GlobalSettings globalSettings, IApplicationCacheService applicationCacheService)
public ProvidersController(
IOrganizationRepository organizationRepository,
IOrganizationService organizationService,
IProviderRepository providerRepository,
IProviderUserRepository providerUserRepository,
IProviderOrganizationRepository providerOrganizationRepository,
IProviderService providerService,
GlobalSettings globalSettings,
IApplicationCacheService applicationCacheService,
IReferenceEventService referenceEventService,
IUserService userService,
ICreateProviderCommand createProviderCommand)
{
_organizationRepository = organizationRepository;
_organizationService = organizationService;
_providerRepository = providerRepository;
_providerUserRepository = providerUserRepository;
_providerOrganizationRepository = providerOrganizationRepository;
_providerService = providerService;
_globalSettings = globalSettings;
_applicationCacheService = applicationCacheService;
_referenceEventService = referenceEventService;
_userService = userService;
_createProviderCommand = createProviderCommand;
}
public async Task<IActionResult> Index(string name = null, string userEmail = null, int page = 1, int count = 25)
@ -75,9 +96,18 @@ public class ProvidersController : Controller
return View(model);
}
await _providerService.CreateAsync(model.OwnerEmail);
var provider = model.ToProvider();
switch (provider.Type)
{
case ProviderType.Msp:
await _createProviderCommand.CreateMspAsync(provider, model.OwnerEmail);
break;
case ProviderType.Reseller:
await _createProviderCommand.CreateResellerAsync(provider);
break;
}
return RedirectToAction("Index");
return RedirectToAction("Edit", new { id = provider.Id });
}
public async Task<IActionResult> View(Guid id)
@ -130,4 +160,76 @@ public class ProvidersController : Controller
TempData["InviteResentTo"] = ownerId;
return RedirectToAction("Edit", new { id = providerId });
}
[HttpGet]
public async Task<IActionResult> AddExistingOrganization(Guid id, string name = null, string ownerEmail = null, int page = 1, int count = 25)
{
if (page < 1)
{
page = 1;
}
if (count < 1)
{
count = 1;
}
var skip = (page - 1) * count;
var unassignedOrganizations = await _organizationRepository.SearchUnassignedToProviderAsync(name, ownerEmail, skip, count);
var viewModel = new OrganizationUnassignedToProviderSearchViewModel
{
OrganizationName = string.IsNullOrWhiteSpace(name) ? null : name,
OrganizationOwnerEmail = string.IsNullOrWhiteSpace(ownerEmail) ? null : ownerEmail,
Page = page,
Count = count,
Items = unassignedOrganizations.Select(uo => new OrganizationSelectableViewModel
{
Id = uo.Id,
Name = uo.Name,
PlanType = uo.PlanType
}).ToList()
};
return View(viewModel);
}
[HttpPost]
public async Task<IActionResult> AddExistingOrganization(Guid id, OrganizationUnassignedToProviderSearchViewModel model)
{
var organizationIds = model.Items.Where(o => o.Selected).Select(o => o.Id).ToArray();
if (organizationIds.Any())
{
await _providerService.AddOrganizationsToReseller(id, organizationIds);
}
return RedirectToAction("Edit", "Providers", new { id = id });
}
[HttpGet]
public async Task<IActionResult> CreateOrganization(Guid providerId)
{
var provider = await _providerRepository.GetByIdAsync(providerId);
if (provider is not { Type: ProviderType.Reseller })
{
return RedirectToAction("Index");
}
return View(new OrganizationEditModel(provider));
}
[HttpPost]
public async Task<IActionResult> CreateOrganization(Guid providerId, OrganizationEditModel model)
{
var provider = await _providerRepository.GetByIdAsync(providerId);
if (provider is not { Type: ProviderType.Reseller })
{
return RedirectToAction("Index");
}
var organization = model.CreateOrganization(provider);
await _organizationService.CreatePendingOrganization(organization, model.Owners, User, _userService, model.SalesAssistedTrialStarted);
await _providerService.AddOrganization(providerId, organization.Id, null);
return RedirectToAction("Edit", "Providers", new { id = providerId });
}
}