using Bit.Admin.Enums; using Bit.Admin.Models; using Bit.Admin.Services; using Bit.Admin.Utilities; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Repositories; 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; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; using Bit.Core.Settings; using Bit.Core.Tools.Enums; using Bit.Core.Tools.Models.Business; using Bit.Core.Tools.Services; using Bit.Core.Utilities; using Bit.Core.Vault.Repositories; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; 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; private readonly ISelfHostedSyncSponsorshipsCommand _syncSponsorshipsCommand; private readonly ICipherRepository _cipherRepository; private readonly ICollectionRepository _collectionRepository; private readonly IGroupRepository _groupRepository; private readonly IPolicyRepository _policyRepository; private readonly IPaymentService _paymentService; private readonly ILicensingService _licensingService; private readonly IApplicationCacheService _applicationCacheService; private readonly GlobalSettings _globalSettings; private readonly IReferenceEventService _referenceEventService; private readonly IUserService _userService; private readonly IProviderRepository _providerRepository; private readonly ILogger _logger; private readonly IAccessControlService _accessControlService; private readonly ICurrentContext _currentContext; private readonly ISecretRepository _secretRepository; private readonly IProjectRepository _projectRepository; private readonly IServiceAccountRepository _serviceAccountRepository; public OrganizationsController( IOrganizationService organizationService, IOrganizationRepository organizationRepository, IOrganizationUserRepository organizationUserRepository, IOrganizationConnectionRepository organizationConnectionRepository, ISelfHostedSyncSponsorshipsCommand syncSponsorshipsCommand, ICipherRepository cipherRepository, ICollectionRepository collectionRepository, IGroupRepository groupRepository, IPolicyRepository policyRepository, IPaymentService paymentService, ILicensingService licensingService, IApplicationCacheService applicationCacheService, GlobalSettings globalSettings, IReferenceEventService referenceEventService, IUserService userService, IProviderRepository providerRepository, ILogger logger, IAccessControlService accessControlService, ICurrentContext currentContext, ISecretRepository secretRepository, IProjectRepository projectRepository, IServiceAccountRepository serviceAccountRepository) { _organizationService = organizationService; _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; _organizationConnectionRepository = organizationConnectionRepository; _syncSponsorshipsCommand = syncSponsorshipsCommand; _cipherRepository = cipherRepository; _collectionRepository = collectionRepository; _groupRepository = groupRepository; _policyRepository = policyRepository; _paymentService = paymentService; _licensingService = licensingService; _applicationCacheService = applicationCacheService; _globalSettings = globalSettings; _referenceEventService = referenceEventService; _userService = userService; _providerRepository = providerRepository; _logger = logger; _accessControlService = accessControlService; _currentContext = currentContext; _secretRepository = secretRepository; _projectRepository = projectRepository; _serviceAccountRepository = serviceAccountRepository; } [RequirePermission(Permission.Org_List_View)] public async Task Index(string name = null, string userEmail = null, bool? paid = null, int page = 1, int count = 25) { if (page < 1) { page = 1; } if (count < 1) { count = 1; } var skip = (page - 1) * count; var organizations = await _organizationRepository.SearchAsync(name, userEmail, paid, skip, count); return View(new OrganizationsModel { Items = organizations as List, Name = string.IsNullOrWhiteSpace(name) ? null : name, UserEmail = string.IsNullOrWhiteSpace(userEmail) ? null : userEmail, Paid = paid, Page = page, Count = count, Action = _globalSettings.SelfHosted ? "View" : "Edit", SelfHosted = _globalSettings.SelfHosted }); } public async Task View(Guid id) { var organization = await _organizationRepository.GetByIdAsync(id); if (organization == null) { return RedirectToAction("Index"); } var provider = await _providerRepository.GetByOrganizationIdAsync(id); var ciphers = await _cipherRepository.GetManyByOrganizationIdAsync(id); var collections = await _collectionRepository.GetManyByOrganizationIdAsync(id); IEnumerable groups = null; if (organization.UseGroups) { groups = await _groupRepository.GetManyByOrganizationIdAsync(id); } IEnumerable policies = null; if (organization.UsePolicies) { policies = await _policyRepository.GetManyByOrganizationIdAsync(id); } var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id); var billingSyncConnection = _globalSettings.EnableCloudCommunication ? await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync) : null; var secrets = organization.UseSecretsManager ? await _secretRepository.GetSecretsCountByOrganizationIdAsync(id) : -1; var projects = organization.UseSecretsManager ? await _projectRepository.GetProjectCountByOrganizationIdAsync(id) : -1; var serviceAccounts = organization.UseSecretsManager ? await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(id) : -1; var smSeats = organization.UseSecretsManager ? await _organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id) : -1; return View(new OrganizationViewModel(organization, provider, billingSyncConnection, users, ciphers, collections, groups, policies, secrets, projects, serviceAccounts, smSeats)); } [SelfHosted(NotSelfHostedOnly = true)] public async Task Edit(Guid id) { var organization = await _organizationRepository.GetByIdAsync(id); if (organization == null) { return RedirectToAction("Index"); } var provider = await _providerRepository.GetByOrganizationIdAsync(id); var ciphers = await _cipherRepository.GetManyByOrganizationIdAsync(id); var collections = await _collectionRepository.GetManyByOrganizationIdAsync(id); IEnumerable groups = null; if (organization.UseGroups) { groups = await _groupRepository.GetManyByOrganizationIdAsync(id); } IEnumerable policies = null; if (organization.UsePolicies) { policies = await _policyRepository.GetManyByOrganizationIdAsync(id); } var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id); var billingInfo = await _paymentService.GetBillingAsync(organization); var billingSyncConnection = _globalSettings.EnableCloudCommunication ? await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync) : null; var secrets = organization.UseSecretsManager ? await _secretRepository.GetSecretsCountByOrganizationIdAsync(id) : -1; var projects = organization.UseSecretsManager ? await _projectRepository.GetProjectCountByOrganizationIdAsync(id) : -1; var serviceAccounts = organization.UseSecretsManager ? await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(id) : -1; var smSeats = organization.UseSecretsManager ? await _organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id) : -1; return View(new OrganizationEditModel(organization, provider, users, ciphers, collections, groups, policies, billingInfo, billingSyncConnection, _globalSettings, secrets, projects, serviceAccounts, smSeats)); } [HttpPost] [ValidateAntiForgeryToken] [SelfHosted(NotSelfHostedOnly = true)] public async Task Edit(Guid id, OrganizationEditModel model) { var organization = await GetOrganization(id, model); if (organization.UseSecretsManager && !organization.SecretsManagerBeta && !StaticStore.GetPlan(organization.PlanType).SupportsSecretsManager) { 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) { EventRaisedByUser = _userService.GetUserName(User), SalesAssistedTrialStarted = model.SalesAssistedTrialStarted, }); return RedirectToAction("Edit", new { id }); } [HttpPost] [ValidateAntiForgeryToken] [RequirePermission(Permission.Org_Delete)] public async Task Delete(Guid id) { var organization = await _organizationRepository.GetByIdAsync(id); if (organization != null) { await _organizationRepository.DeleteAsync(organization); await _applicationCacheService.DeleteOrganizationAbilityAsync(organization.Id); } return RedirectToAction("Index"); } public async Task TriggerBillingSync(Guid id) { var organization = await _organizationRepository.GetByIdAsync(id); if (organization == null) { return RedirectToAction("Index"); } var connection = (await _organizationConnectionRepository.GetEnabledByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync)).FirstOrDefault(); if (connection != null) { try { var config = connection.GetConfig(); await _syncSponsorshipsCommand.SyncOrganization(id, config.CloudOrganizationId, connection); TempData["ConnectionActivated"] = id; TempData["ConnectionError"] = null; } catch (Exception ex) { TempData["ConnectionError"] = ex.Message; _logger.LogWarning(ex, "Error while attempting to do billing sync for organization with id '{OrganizationId}'", id); } if (_globalSettings.SelfHosted) { return RedirectToAction("View", new { id }); } else { return RedirectToAction("Edit", new { id }); } } return RedirectToAction("Index"); } [HttpPost] public async Task 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); } private async Task GetOrganization(Guid id, OrganizationEditModel model) { var organization = await _organizationRepository.GetByIdAsync(id); if (_accessControlService.UserHasPermission(Permission.Org_CheckEnabledBox)) { organization.Enabled = model.Enabled; } if (_accessControlService.UserHasPermission(Permission.Org_Plan_Edit)) { organization.PlanType = model.PlanType.Value; organization.Plan = model.Plan; organization.Seats = model.Seats; organization.MaxAutoscaleSeats = model.MaxAutoscaleSeats; organization.MaxCollections = model.MaxCollections; organization.MaxStorageGb = model.MaxStorageGb; //features organization.SelfHost = model.SelfHost; organization.Use2fa = model.Use2fa; organization.UseApi = model.UseApi; organization.UseGroups = model.UseGroups; organization.UsePolicies = model.UsePolicies; organization.UseSso = model.UseSso; organization.UseKeyConnector = model.UseKeyConnector; organization.UseScim = model.UseScim; organization.UseDirectory = model.UseDirectory; organization.UseEvents = model.UseEvents; organization.UseResetPassword = model.UseResetPassword; organization.UseCustomPermissions = model.UseCustomPermissions; organization.UseTotp = model.UseTotp; organization.UsersGetPremium = model.UsersGetPremium; organization.UseSecretsManager = model.UseSecretsManager; organization.SecretsManagerBeta = model.SecretsManagerBeta; //secrets organization.SmSeats = model.SmSeats; organization.MaxAutoscaleSmSeats = model.MaxAutoscaleSmSeats; organization.SmServiceAccounts = model.SmServiceAccounts; organization.MaxAutoscaleSmServiceAccounts = model.MaxAutoscaleSmServiceAccounts; } if (_accessControlService.UserHasPermission(Permission.Org_Licensing_Edit)) { organization.LicenseKey = model.LicenseKey; organization.ExpirationDate = model.ExpirationDate; } if (_accessControlService.UserHasPermission(Permission.Org_Billing_Edit)) { organization.BillingEmail = model.BillingEmail?.ToLowerInvariant()?.Trim(); organization.Gateway = model.Gateway; organization.GatewayCustomerId = model.GatewayCustomerId; organization.GatewaySubscriptionId = model.GatewaySubscriptionId; } return organization; } }