mirror of
https://github.com/bitwarden/server.git
synced 2025-05-03 10:42:21 -05:00
Merge branch 'main' into feature/phishing-detection
This commit is contained in:
commit
de248609f1
@ -110,9 +110,14 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
|
|||||||
IEnumerable<string> organizationOwnerEmails)
|
IEnumerable<string> organizationOwnerEmails)
|
||||||
{
|
{
|
||||||
if (provider.IsBillable() &&
|
if (provider.IsBillable() &&
|
||||||
organization.IsValidClient() &&
|
organization.IsValidClient())
|
||||||
!string.IsNullOrEmpty(organization.GatewayCustomerId))
|
|
||||||
{
|
{
|
||||||
|
// An organization converted to a business unit will not have a Customer since it was given to the business unit.
|
||||||
|
if (string.IsNullOrEmpty(organization.GatewayCustomerId))
|
||||||
|
{
|
||||||
|
await _providerBillingService.CreateCustomerForClientOrganization(provider, organization);
|
||||||
|
}
|
||||||
|
|
||||||
var customer = await _stripeAdapter.CustomerUpdateAsync(organization.GatewayCustomerId, new CustomerUpdateOptions
|
var customer = await _stripeAdapter.CustomerUpdateAsync(organization.GatewayCustomerId, new CustomerUpdateOptions
|
||||||
{
|
{
|
||||||
Description = string.Empty,
|
Description = string.Empty,
|
||||||
|
@ -21,7 +21,6 @@ using Bit.Core.Models.Business;
|
|||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using CsvHelper;
|
using CsvHelper;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@ -46,7 +45,6 @@ public class ProviderBillingService(
|
|||||||
[FromKeyedServices(AutomaticTaxFactory.BusinessUse)] IAutomaticTaxStrategy automaticTaxStrategy)
|
[FromKeyedServices(AutomaticTaxFactory.BusinessUse)] IAutomaticTaxStrategy automaticTaxStrategy)
|
||||||
: IProviderBillingService
|
: IProviderBillingService
|
||||||
{
|
{
|
||||||
[RequireFeature(FeatureFlagKeys.P15179_AddExistingOrgsFromProviderPortal)]
|
|
||||||
public async Task AddExistingOrganization(
|
public async Task AddExistingOrganization(
|
||||||
Provider provider,
|
Provider provider,
|
||||||
Organization organization,
|
Organization organization,
|
||||||
@ -312,7 +310,6 @@ public class ProviderBillingService(
|
|||||||
return memoryStream.ToArray();
|
return memoryStream.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
[RequireFeature(FeatureFlagKeys.P15179_AddExistingOrgsFromProviderPortal)]
|
|
||||||
public async Task<IEnumerable<AddableOrganization>> GetAddableOrganizations(
|
public async Task<IEnumerable<AddableOrganization>> GetAddableOrganizations(
|
||||||
Provider provider,
|
Provider provider,
|
||||||
Guid userId)
|
Guid userId)
|
||||||
|
@ -470,6 +470,19 @@ public class ProvidersController : Controller
|
|||||||
[RequirePermission(Permission.Provider_Edit)]
|
[RequirePermission(Permission.Provider_Edit)]
|
||||||
public async Task<IActionResult> Delete(Guid id, string providerName)
|
public async Task<IActionResult> Delete(Guid id, string providerName)
|
||||||
{
|
{
|
||||||
|
var provider = await _providerRepository.GetByIdAsync(id);
|
||||||
|
|
||||||
|
if (provider is null)
|
||||||
|
{
|
||||||
|
return BadRequest("Provider does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (provider.Status == ProviderStatusType.Pending)
|
||||||
|
{
|
||||||
|
await _providerService.DeleteAsync(provider);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(providerName))
|
if (string.IsNullOrWhiteSpace(providerName))
|
||||||
{
|
{
|
||||||
return BadRequest("Invalid provider name");
|
return BadRequest("Invalid provider name");
|
||||||
@ -482,13 +495,6 @@ public class ProvidersController : Controller
|
|||||||
return BadRequest("You must unlink all clients before you can delete a provider");
|
return BadRequest("You must unlink all clients before you can delete a provider");
|
||||||
}
|
}
|
||||||
|
|
||||||
var provider = await _providerRepository.GetByIdAsync(id);
|
|
||||||
|
|
||||||
if (provider is null)
|
|
||||||
{
|
|
||||||
return BadRequest("Provider does not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.Equals(providerName.Trim(), provider.DisplayName(), StringComparison.OrdinalIgnoreCase))
|
if (!string.Equals(providerName.Trim(), provider.DisplayName(), StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return BadRequest("Invalid provider name");
|
return BadRequest("Invalid provider name");
|
||||||
|
@ -183,17 +183,29 @@
|
|||||||
<div class="p-3">
|
<div class="p-3">
|
||||||
<h4 class="fw-bolder" id="exampleModalLabel">Delete provider</h4>
|
<h4 class="fw-bolder" id="exampleModalLabel">Delete provider</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
|
||||||
<span class="fw-light">
|
@if (Model.Provider.Status == ProviderStatusType.Pending)
|
||||||
This action is permanent and irreversible. Enter the provider name to complete deletion of the provider and associated data.
|
{
|
||||||
</span>
|
<div class="modal-body">
|
||||||
<form>
|
<span class="fw-light">
|
||||||
<div class="mb-3">
|
This action is permanent and irreversible.
|
||||||
<label for="provider-name" class="col-form-label">Provider name</label>
|
</span>
|
||||||
<input type="text" class="form-control" id="provider-name">
|
</div>
|
||||||
</div>
|
}
|
||||||
</form>
|
else
|
||||||
</div>
|
{
|
||||||
|
<div class="modal-body">
|
||||||
|
<span class="fw-light">
|
||||||
|
This action is permanent and irreversible. Enter the provider name to complete deletion of the provider and associated data.
|
||||||
|
</span>
|
||||||
|
<form>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="provider-name" class="col-form-label">Provider name</label>
|
||||||
|
<input type="text" class="form-control" id="provider-name">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline-primary btn-pill" data-bs-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-outline-primary btn-pill" data-bs-dismiss="modal">Cancel</button>
|
||||||
<button type="button" class="btn btn-danger btn-pill" onclick="deleteProvider('@Model.Provider.Id');">Delete provider</button>
|
<button type="button" class="btn btn-danger btn-pill" onclick="deleteProvider('@Model.Provider.Id');">Delete provider</button>
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
||||||
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
||||||
|
using Bit.Core;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Bit.Api.AdminConsole.Controllers;
|
namespace Bit.Api.AdminConsole.Controllers;
|
||||||
|
|
||||||
|
[RequireFeature(FeatureFlagKeys.EventBasedOrganizationIntegrations)]
|
||||||
[Route("organizations/{organizationId:guid}/integrations/{integrationId:guid}/configurations")]
|
[Route("organizations/{organizationId:guid}/integrations/{integrationId:guid}/configurations")]
|
||||||
[Authorize("Application")]
|
[Authorize("Application")]
|
||||||
public class OrganizationIntegrationConfigurationController(
|
public class OrganizationIntegrationConfigurationController(
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
||||||
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
||||||
|
using Bit.Core;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@ -10,6 +12,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
|
|
||||||
namespace Bit.Api.AdminConsole.Controllers;
|
namespace Bit.Api.AdminConsole.Controllers;
|
||||||
|
|
||||||
|
[RequireFeature(FeatureFlagKeys.EventBasedOrganizationIntegrations)]
|
||||||
[Route("organizations/{organizationId:guid}/integrations")]
|
[Route("organizations/{organizationId:guid}/integrations")]
|
||||||
[Authorize("Application")]
|
[Authorize("Application")]
|
||||||
public class OrganizationIntegrationController(
|
public class OrganizationIntegrationController(
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using Bit.Api.Billing.Controllers;
|
using Bit.Api.Billing.Controllers;
|
||||||
using Bit.Api.Billing.Models.Requests;
|
using Bit.Api.Billing.Models.Requests;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Services;
|
||||||
@ -17,7 +16,6 @@ namespace Bit.Api.AdminConsole.Controllers;
|
|||||||
[Route("providers/{providerId:guid}/clients")]
|
[Route("providers/{providerId:guid}/clients")]
|
||||||
public class ProviderClientsController(
|
public class ProviderClientsController(
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
IFeatureService featureService,
|
|
||||||
ILogger<BaseProviderController> logger,
|
ILogger<BaseProviderController> logger,
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
IProviderBillingService providerBillingService,
|
IProviderBillingService providerBillingService,
|
||||||
@ -140,11 +138,6 @@ public class ProviderClientsController(
|
|||||||
[SelfHosted(NotSelfHostedOnly = true)]
|
[SelfHosted(NotSelfHostedOnly = true)]
|
||||||
public async Task<IResult> GetAddableOrganizationsAsync([FromRoute] Guid providerId)
|
public async Task<IResult> GetAddableOrganizationsAsync([FromRoute] Guid providerId)
|
||||||
{
|
{
|
||||||
if (!featureService.IsEnabled(FeatureFlagKeys.P15179_AddExistingOrgsFromProviderPortal))
|
|
||||||
{
|
|
||||||
return Error.NotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
var (provider, result) = await TryGetBillableProviderForServiceUserOperation(providerId);
|
var (provider, result) = await TryGetBillableProviderForServiceUserOperation(providerId);
|
||||||
|
|
||||||
if (provider == null)
|
if (provider == null)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
||||||
|
using Bit.Core;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -7,11 +8,13 @@ using Bit.Core.Exceptions;
|
|||||||
using Bit.Core.Models.Data.Integrations;
|
using Bit.Core.Models.Data.Integrations;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Bit.Api.AdminConsole.Controllers;
|
namespace Bit.Api.AdminConsole.Controllers;
|
||||||
|
|
||||||
|
[RequireFeature(FeatureFlagKeys.EventBasedOrganizationIntegrations)]
|
||||||
[Route("organizations/{organizationId:guid}/integrations/slack")]
|
[Route("organizations/{organizationId:guid}/integrations/slack")]
|
||||||
[Authorize("Application")]
|
[Authorize("Application")]
|
||||||
public class SlackIntegrationController(
|
public class SlackIntegrationController(
|
||||||
|
@ -221,8 +221,7 @@ public class MembersController : Controller
|
|||||||
/// Remove a member.
|
/// Remove a member.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Permanently removes a member from the organization. This cannot be undone.
|
/// Removes a member from the organization. This cannot be undone. The user account will still remain.
|
||||||
/// The user account will still remain. The user is only removed from the organization.
|
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="id">The identifier of the member to be removed.</param>
|
/// <param name="id">The identifier of the member to be removed.</param>
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
|
@ -44,7 +44,7 @@ public class InviteUsersPasswordManagerValidator(
|
|||||||
return new Invalid<PasswordManagerSubscriptionUpdate>(new PasswordManagerMustHaveSeatsError(subscriptionUpdate));
|
return new Invalid<PasswordManagerSubscriptionUpdate>(new PasswordManagerMustHaveSeatsError(subscriptionUpdate));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subscriptionUpdate.MaxSeatsReached)
|
if (subscriptionUpdate.MaxSeatsExceeded)
|
||||||
{
|
{
|
||||||
return new Invalid<PasswordManagerSubscriptionUpdate>(
|
return new Invalid<PasswordManagerSubscriptionUpdate>(
|
||||||
new PasswordManagerSeatLimitHasBeenReachedError(subscriptionUpdate));
|
new PasswordManagerSeatLimitHasBeenReachedError(subscriptionUpdate));
|
||||||
|
@ -48,6 +48,11 @@ public class PasswordManagerSubscriptionUpdate
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool MaxSeatsReached => UpdatedSeatTotal.HasValue && MaxAutoScaleSeats.HasValue && UpdatedSeatTotal.Value >= MaxAutoScaleSeats.Value;
|
public bool MaxSeatsReached => UpdatedSeatTotal.HasValue && MaxAutoScaleSeats.HasValue && UpdatedSeatTotal.Value >= MaxAutoScaleSeats.Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the new seat total exceeds the organization's auto-scale seat limit
|
||||||
|
/// </summary>
|
||||||
|
public bool MaxSeatsExceeded => UpdatedSeatTotal.HasValue && MaxAutoScaleSeats.HasValue && UpdatedSeatTotal.Value > MaxAutoScaleSeats.Value;
|
||||||
|
|
||||||
public Plan.PasswordManagerPlanFeatures PasswordManagerPlan { get; }
|
public Plan.PasswordManagerPlanFeatures PasswordManagerPlan { get; }
|
||||||
|
|
||||||
public InviteOrganization InviteOrganization { get; }
|
public InviteOrganization InviteOrganization { get; }
|
||||||
|
@ -41,6 +41,7 @@ public static class OrganizationLicenseConstants
|
|||||||
public const string Refresh = nameof(Refresh);
|
public const string Refresh = nameof(Refresh);
|
||||||
public const string ExpirationWithoutGracePeriod = nameof(ExpirationWithoutGracePeriod);
|
public const string ExpirationWithoutGracePeriod = nameof(ExpirationWithoutGracePeriod);
|
||||||
public const string Trial = nameof(Trial);
|
public const string Trial = nameof(Trial);
|
||||||
|
public const string UseAdminSponsoredFamilies = nameof(UseAdminSponsoredFamilies);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UserLicenseConstants
|
public static class UserLicenseConstants
|
||||||
|
@ -53,6 +53,7 @@ public class OrganizationLicenseClaimsFactory : ILicenseClaimsFactory<Organizati
|
|||||||
new(nameof(OrganizationLicenseConstants.Refresh), refresh.ToString(CultureInfo.InvariantCulture)),
|
new(nameof(OrganizationLicenseConstants.Refresh), refresh.ToString(CultureInfo.InvariantCulture)),
|
||||||
new(nameof(OrganizationLicenseConstants.ExpirationWithoutGracePeriod), expirationWithoutGracePeriod.ToString(CultureInfo.InvariantCulture)),
|
new(nameof(OrganizationLicenseConstants.ExpirationWithoutGracePeriod), expirationWithoutGracePeriod.ToString(CultureInfo.InvariantCulture)),
|
||||||
new(nameof(OrganizationLicenseConstants.Trial), trial.ToString()),
|
new(nameof(OrganizationLicenseConstants.Trial), trial.ToString()),
|
||||||
|
new(nameof(OrganizationLicenseConstants.UseAdminSponsoredFamilies), entity.UseAdminSponsoredFamilies.ToString()),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (entity.Name is not null)
|
if (entity.Name is not null)
|
||||||
@ -109,6 +110,7 @@ public class OrganizationLicenseClaimsFactory : ILicenseClaimsFactory<Organizati
|
|||||||
{
|
{
|
||||||
claims.Add(new Claim(nameof(OrganizationLicenseConstants.SmServiceAccounts), entity.SmServiceAccounts.ToString()));
|
claims.Add(new Claim(nameof(OrganizationLicenseConstants.SmServiceAccounts), entity.SmServiceAccounts.ToString()));
|
||||||
}
|
}
|
||||||
|
claims.Add(new Claim(nameof(OrganizationLicenseConstants.UseAdminSponsoredFamilies), entity.UseAdminSponsoredFamilies.ToString()));
|
||||||
|
|
||||||
return Task.FromResult(claims);
|
return Task.FromResult(claims);
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,7 @@ public static class FeatureFlagKeys
|
|||||||
public const string PolicyRequirements = "pm-14439-policy-requirements";
|
public const string PolicyRequirements = "pm-14439-policy-requirements";
|
||||||
public const string SsoExternalIdVisibility = "pm-18630-sso-external-id-visibility";
|
public const string SsoExternalIdVisibility = "pm-18630-sso-external-id-visibility";
|
||||||
public const string ScimInviteUserOptimization = "pm-16811-optimize-invite-user-flow-to-fail-fast";
|
public const string ScimInviteUserOptimization = "pm-16811-optimize-invite-user-flow-to-fail-fast";
|
||||||
|
public const string EventBasedOrganizationIntegrations = "event-based-organization-integrations";
|
||||||
|
|
||||||
/* Auth Team */
|
/* Auth Team */
|
||||||
public const string PM9112DeviceApprovalPersistence = "pm-9112-device-approval-persistence";
|
public const string PM9112DeviceApprovalPersistence = "pm-9112-device-approval-persistence";
|
||||||
@ -142,7 +143,6 @@ public static class FeatureFlagKeys
|
|||||||
public const string TrialPayment = "PM-8163-trial-payment";
|
public const string TrialPayment = "PM-8163-trial-payment";
|
||||||
public const string PM17772_AdminInitiatedSponsorships = "pm-17772-admin-initiated-sponsorships";
|
public const string PM17772_AdminInitiatedSponsorships = "pm-17772-admin-initiated-sponsorships";
|
||||||
public const string UsePricingService = "use-pricing-service";
|
public const string UsePricingService = "use-pricing-service";
|
||||||
public const string P15179_AddExistingOrgsFromProviderPortal = "pm-15179-add-existing-orgs-from-provider-portal";
|
|
||||||
public const string PM12276Breadcrumbing = "pm-12276-breadcrumbing-for-business-features";
|
public const string PM12276Breadcrumbing = "pm-12276-breadcrumbing-for-business-features";
|
||||||
public const string PM18794_ProviderPaymentMethod = "pm-18794-provider-payment-method";
|
public const string PM18794_ProviderPaymentMethod = "pm-18794-provider-payment-method";
|
||||||
public const string PM19147_AutomaticTaxImprovements = "pm-19147-automatic-tax-improvements";
|
public const string PM19147_AutomaticTaxImprovements = "pm-19147-automatic-tax-improvements";
|
||||||
@ -150,6 +150,10 @@ public static class FeatureFlagKeys
|
|||||||
public const string PM18770_EnableOrganizationBusinessUnitConversion = "pm-18770-enable-organization-business-unit-conversion";
|
public const string PM18770_EnableOrganizationBusinessUnitConversion = "pm-18770-enable-organization-business-unit-conversion";
|
||||||
public const string PM199566_UpdateMSPToChargeAutomatically = "pm-199566-update-msp-to-charge-automatically";
|
public const string PM199566_UpdateMSPToChargeAutomatically = "pm-199566-update-msp-to-charge-automatically";
|
||||||
|
|
||||||
|
/* Data Insights and Reporting Team */
|
||||||
|
public const string RiskInsightsCriticalApplication = "pm-14466-risk-insights-critical-application";
|
||||||
|
public const string EnableRiskInsightsNotifications = "enable-risk-insights-notifications";
|
||||||
|
|
||||||
/* Key Management Team */
|
/* Key Management Team */
|
||||||
public const string ReturnErrorOnExistingKeypair = "return-error-on-existing-keypair";
|
public const string ReturnErrorOnExistingKeypair = "return-error-on-existing-keypair";
|
||||||
public const string PM4154BulkEncryptionService = "PM-4154-bulk-encryption-service";
|
public const string PM4154BulkEncryptionService = "PM-4154-bulk-encryption-service";
|
||||||
@ -186,8 +190,6 @@ public static class FeatureFlagKeys
|
|||||||
|
|
||||||
/* Tools Team */
|
/* Tools Team */
|
||||||
public const string ItemShare = "item-share";
|
public const string ItemShare = "item-share";
|
||||||
public const string RiskInsightsCriticalApplication = "pm-14466-risk-insights-critical-application";
|
|
||||||
public const string EnableRiskInsightsNotifications = "enable-risk-insights-notifications";
|
|
||||||
public const string DesktopSendUIRefresh = "desktop-send-ui-refresh";
|
public const string DesktopSendUIRefresh = "desktop-send-ui-refresh";
|
||||||
|
|
||||||
/* Vault Team */
|
/* Vault Team */
|
||||||
@ -195,7 +197,6 @@ public static class FeatureFlagKeys
|
|||||||
public const string PM9111ExtensionPersistAddEditForm = "pm-9111-extension-persist-add-edit-form";
|
public const string PM9111ExtensionPersistAddEditForm = "pm-9111-extension-persist-add-edit-form";
|
||||||
public const string NewDeviceVerificationPermanentDismiss = "new-device-permanent-dismiss";
|
public const string NewDeviceVerificationPermanentDismiss = "new-device-permanent-dismiss";
|
||||||
public const string NewDeviceVerificationTemporaryDismiss = "new-device-temporary-dismiss";
|
public const string NewDeviceVerificationTemporaryDismiss = "new-device-temporary-dismiss";
|
||||||
public const string VaultBulkManagementAction = "vault-bulk-management-action";
|
|
||||||
public const string RestrictProviderAccess = "restrict-provider-access";
|
public const string RestrictProviderAccess = "restrict-provider-access";
|
||||||
public const string SecurityTasks = "security-tasks";
|
public const string SecurityTasks = "security-tasks";
|
||||||
public const string CipherKeyEncryption = "cipher-key-encryption";
|
public const string CipherKeyEncryption = "cipher-key-encryption";
|
||||||
|
@ -23,8 +23,8 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AspNetCoreRateLimit.Redis" Version="2.0.0" />
|
<PackageReference Include="AspNetCoreRateLimit.Redis" Version="2.0.0" />
|
||||||
<PackageReference Include="AWSSDK.SimpleEmail" Version="3.7.402.61" />
|
<PackageReference Include="AWSSDK.SimpleEmail" Version="3.7.402.79" />
|
||||||
<PackageReference Include="AWSSDK.SQS" Version="3.7.400.118" />
|
<PackageReference Include="AWSSDK.SQS" Version="3.7.400.136" />
|
||||||
<PackageReference Include="Azure.Data.Tables" Version="12.9.0" />
|
<PackageReference Include="Azure.Data.Tables" Version="12.9.0" />
|
||||||
<PackageReference Include="Azure.Extensions.AspNetCore.DataProtection.Blobs" Version="1.3.4" />
|
<PackageReference Include="Azure.Extensions.AspNetCore.DataProtection.Blobs" Version="1.3.4" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="8.0.10" />
|
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="8.0.10" />
|
||||||
@ -38,7 +38,7 @@
|
|||||||
<PackageReference Include="Handlebars.Net" Version="2.1.6" />
|
<PackageReference Include="Handlebars.Net" Version="2.1.6" />
|
||||||
<PackageReference Include="MailKit" Version="4.11.0" />
|
<PackageReference Include="MailKit" Version="4.11.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" />
|
||||||
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.46.1" />
|
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.49.0" />
|
||||||
<PackageReference Include="Microsoft.Azure.NotificationHubs" Version="4.2.0" />
|
<PackageReference Include="Microsoft.Azure.NotificationHubs" Version="4.2.0" />
|
||||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.2" />
|
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Cosmos" Version="1.7.0" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Cosmos" Version="1.7.0" />
|
||||||
|
@ -19,6 +19,34 @@ public class OrganizationLicense : ILicense
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="OrganizationLicense"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// ⚠️ DEPRECATED: This constructor and the entire property-based licensing system is deprecated.
|
||||||
|
/// Do not add new properties to this constructor or extend its functionality.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// This implementation has been replaced by a new claims-based licensing system that provides better security
|
||||||
|
/// and flexibility. The new system uses JWT claims to store and validate license information, making it more
|
||||||
|
/// secure and easier to extend without requiring changes to the license format.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// For new license-related features or modifications:
|
||||||
|
/// 1. Use the claims-based system instead of adding properties here
|
||||||
|
/// 2. Add new claims to the license token
|
||||||
|
/// 3. Validate claims in the <see cref="CanUse"/> and <see cref="VerifyData"/> methods
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// This constructor is maintained only for backward compatibility with existing licenses.
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="org">The organization to create the license for.</param>
|
||||||
|
/// <param name="subscriptionInfo">Information about the organization's subscription.</param>
|
||||||
|
/// <param name="installationId">The ID of the current installation.</param>
|
||||||
|
/// <param name="licenseService">The service used to sign the license.</param>
|
||||||
|
/// <param name="version">Optional version number for the license format.</param>
|
||||||
public OrganizationLicense(Organization org, SubscriptionInfo subscriptionInfo, Guid installationId,
|
public OrganizationLicense(Organization org, SubscriptionInfo subscriptionInfo, Guid installationId,
|
||||||
ILicensingService licenseService, int? version = null)
|
ILicensingService licenseService, int? version = null)
|
||||||
{
|
{
|
||||||
@ -105,6 +133,7 @@ public class OrganizationLicense : ILicense
|
|||||||
Trial = false;
|
Trial = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UseAdminSponsoredFamilies = org.UseAdminSponsoredFamilies;
|
||||||
Hash = Convert.ToBase64String(ComputeHash());
|
Hash = Convert.ToBase64String(ComputeHash());
|
||||||
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
|
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
|
||||||
}
|
}
|
||||||
@ -153,6 +182,7 @@ public class OrganizationLicense : ILicense
|
|||||||
|
|
||||||
public bool Trial { get; set; }
|
public bool Trial { get; set; }
|
||||||
public LicenseType? LicenseType { get; set; }
|
public LicenseType? LicenseType { get; set; }
|
||||||
|
public bool UseAdminSponsoredFamilies { get; set; }
|
||||||
public string Hash { get; set; }
|
public string Hash { get; set; }
|
||||||
public string Signature { get; set; }
|
public string Signature { get; set; }
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
@ -292,13 +322,35 @@ public class OrganizationLicense : ILicense
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Do not extend this method. It is only here for backwards compatibility with old licenses.
|
/// Validates an obsolete license format using property-based validation.
|
||||||
/// Instead, extend the CanUse method using the ClaimsPrincipal.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="globalSettings"></param>
|
/// <remarks>
|
||||||
/// <param name="licensingService"></param>
|
/// <para>
|
||||||
/// <param name="exception"></param>
|
/// ⚠️ DEPRECATED: This method is deprecated and should not be extended or modified.
|
||||||
/// <returns></returns>
|
/// It is maintained only for backward compatibility with old license formats.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// This method has been replaced by a new claims-based validation system that provides:
|
||||||
|
/// - Better security through JWT claims
|
||||||
|
/// - More flexible validation rules
|
||||||
|
/// - Easier extensibility without changing the license format
|
||||||
|
/// - Better separation of concerns
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// To add new license validation rules:
|
||||||
|
/// 1. Add new claims to the license token in the claims-based system
|
||||||
|
/// 2. Extend the <see cref="CanUse(IGlobalSettings, ILicensingService, ClaimsPrincipal, out string)"/> method
|
||||||
|
/// 3. Validate the new claims using the ClaimsPrincipal parameter
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// This method will be removed in a future version once all old licenses have been migrated
|
||||||
|
/// to the new claims-based system.
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="globalSettings">The global settings containing installation information.</param>
|
||||||
|
/// <param name="licensingService">The service used to verify the license signature.</param>
|
||||||
|
/// <param name="exception">When the method returns false, contains the error message explaining why the license is invalid.</param>
|
||||||
|
/// <returns>True if the license is valid, false otherwise.</returns>
|
||||||
private bool ObsoleteCanUse(IGlobalSettings globalSettings, ILicensingService licensingService, out string exception)
|
private bool ObsoleteCanUse(IGlobalSettings globalSettings, ILicensingService licensingService, out string exception)
|
||||||
{
|
{
|
||||||
// Do not extend this method. It is only here for backwards compatibility with old licenses.
|
// Do not extend this method. It is only here for backwards compatibility with old licenses.
|
||||||
@ -392,6 +444,7 @@ public class OrganizationLicense : ILicense
|
|||||||
var usePasswordManager = claimsPrincipal.GetValue<bool>(nameof(UsePasswordManager));
|
var usePasswordManager = claimsPrincipal.GetValue<bool>(nameof(UsePasswordManager));
|
||||||
var smSeats = claimsPrincipal.GetValue<int?>(nameof(SmSeats));
|
var smSeats = claimsPrincipal.GetValue<int?>(nameof(SmSeats));
|
||||||
var smServiceAccounts = claimsPrincipal.GetValue<int?>(nameof(SmServiceAccounts));
|
var smServiceAccounts = claimsPrincipal.GetValue<int?>(nameof(SmServiceAccounts));
|
||||||
|
var useAdminSponsoredFamilies = claimsPrincipal.GetValue<bool>(nameof(UseAdminSponsoredFamilies));
|
||||||
|
|
||||||
return issued <= DateTime.UtcNow &&
|
return issued <= DateTime.UtcNow &&
|
||||||
expires >= DateTime.UtcNow &&
|
expires >= DateTime.UtcNow &&
|
||||||
@ -419,7 +472,9 @@ public class OrganizationLicense : ILicense
|
|||||||
useSecretsManager == organization.UseSecretsManager &&
|
useSecretsManager == organization.UseSecretsManager &&
|
||||||
usePasswordManager == organization.UsePasswordManager &&
|
usePasswordManager == organization.UsePasswordManager &&
|
||||||
smSeats == organization.SmSeats &&
|
smSeats == organization.SmSeats &&
|
||||||
smServiceAccounts == organization.SmServiceAccounts;
|
smServiceAccounts == organization.SmServiceAccounts &&
|
||||||
|
useAdminSponsoredFamilies == organization.UseAdminSponsoredFamilies;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using Bit.Core;
|
||||||
using Bit.Core.AdminConsole.Services.Implementations;
|
using Bit.Core.AdminConsole.Services.Implementations;
|
||||||
using Bit.Core.AdminConsole.Services.NoopImplementations;
|
using Bit.Core.AdminConsole.Services.NoopImplementations;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
@ -62,33 +63,45 @@ public class Startup
|
|||||||
{
|
{
|
||||||
services.AddSingleton<IApplicationCacheService, InMemoryApplicationCacheService>();
|
services.AddSingleton<IApplicationCacheService, InMemoryApplicationCacheService>();
|
||||||
}
|
}
|
||||||
services.AddScoped<IEventService, EventService>();
|
|
||||||
if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString))
|
if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString))
|
||||||
{
|
{
|
||||||
|
services.AddKeyedSingleton<IEventWriteService, AzureQueueEventWriteService>("storage");
|
||||||
|
|
||||||
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) &&
|
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) &&
|
||||||
CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.TopicName))
|
CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.TopicName))
|
||||||
{
|
{
|
||||||
services.AddSingleton<IEventWriteService, AzureServiceBusEventWriteService>();
|
services.AddKeyedSingleton<IEventWriteService, AzureServiceBusEventWriteService>("broadcast");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
services.AddSingleton<IEventWriteService, AzureQueueEventWriteService>();
|
services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
services.AddKeyedSingleton<IEventWriteService, RepositoryEventWriteService>("storage");
|
||||||
|
|
||||||
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HostName) &&
|
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HostName) &&
|
||||||
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Username) &&
|
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Username) &&
|
||||||
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Password) &&
|
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Password) &&
|
||||||
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.ExchangeName))
|
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.ExchangeName))
|
||||||
{
|
{
|
||||||
services.AddSingleton<IEventWriteService, RabbitMqEventWriteService>();
|
services.AddKeyedSingleton<IEventWriteService, RabbitMqEventWriteService>("broadcast");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
services.AddSingleton<IEventWriteService, RepositoryEventWriteService>();
|
services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
services.AddScoped<IEventWriteService>(sp =>
|
||||||
|
{
|
||||||
|
var featureService = sp.GetRequiredService<IFeatureService>();
|
||||||
|
var key = featureService.IsEnabled(FeatureFlagKeys.EventBasedOrganizationIntegrations)
|
||||||
|
? "broadcast" : "storage";
|
||||||
|
return sp.GetRequiredKeyedService<IEventWriteService>(key);
|
||||||
|
});
|
||||||
|
services.AddScoped<IEventService, EventService>();
|
||||||
|
|
||||||
services.AddOptionality();
|
services.AddOptionality();
|
||||||
|
|
||||||
|
@ -150,6 +150,8 @@ public static class DapperHelpers
|
|||||||
os => os.LastSyncDate,
|
os => os.LastSyncDate,
|
||||||
os => os.ValidUntil,
|
os => os.ValidUntil,
|
||||||
os => os.ToDelete,
|
os => os.ToDelete,
|
||||||
|
os => os.IsAdminInitiated,
|
||||||
|
os => os.Notes,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dapper" Version="2.1.35" />
|
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -23,7 +23,19 @@ public class OrganizationUserReadOccupiedSeatCountByOrganizationIdQuery : IQuery
|
|||||||
var sponsorshipsQuery = from os in dbContext.OrganizationSponsorships
|
var sponsorshipsQuery = from os in dbContext.OrganizationSponsorships
|
||||||
where os.SponsoringOrganizationId == _organizationId &&
|
where os.SponsoringOrganizationId == _organizationId &&
|
||||||
os.IsAdminInitiated &&
|
os.IsAdminInitiated &&
|
||||||
!os.ToDelete
|
(
|
||||||
|
// Not marked for deletion - always count
|
||||||
|
(!os.ToDelete) ||
|
||||||
|
// Marked for deletion but has a valid until date in the future (RevokeWhenExpired status)
|
||||||
|
(os.ToDelete && os.ValidUntil.HasValue && os.ValidUntil.Value > DateTime.UtcNow)
|
||||||
|
) &&
|
||||||
|
(
|
||||||
|
// SENT status: When SponsoredOrganizationId is null
|
||||||
|
os.SponsoredOrganizationId == null ||
|
||||||
|
// ACCEPTED status: When SponsoredOrganizationId is not null and ValidUntil is null or in the future
|
||||||
|
(os.SponsoredOrganizationId != null &&
|
||||||
|
(!os.ValidUntil.HasValue || os.ValidUntil.Value > DateTime.UtcNow))
|
||||||
|
)
|
||||||
select new OrganizationUser
|
select new OrganizationUser
|
||||||
{
|
{
|
||||||
Id = os.Id,
|
Id = os.Id,
|
||||||
|
@ -8,12 +8,12 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||||
<PackageReference Include="linq2db" Version="5.4.1" />
|
<PackageReference Include="linq2db" Version="5.4.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[8.0.8]" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[8.0.8]" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="[8.0.8]" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="[8.0.4]" />
|
||||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="[8.0.2]" />
|
||||||
<PackageReference Include="linq2db.EntityFrameworkCore" Version="8.1.0" />
|
<PackageReference Include="linq2db.EntityFrameworkCore" Version="[8.1.0]" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -4,6 +4,7 @@ using System.Security.Claims;
|
|||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using AspNetCoreRateLimit;
|
using AspNetCoreRateLimit;
|
||||||
using Azure.Storage.Queues;
|
using Azure.Storage.Queues;
|
||||||
|
using Bit.Core;
|
||||||
using Bit.Core.AdminConsole.Models.Business.Tokenables;
|
using Bit.Core.AdminConsole.Models.Business.Tokenables;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
@ -332,34 +333,46 @@ public static class ServiceCollectionExtensions
|
|||||||
|
|
||||||
if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString))
|
if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString))
|
||||||
{
|
{
|
||||||
|
services.AddKeyedSingleton<IEventWriteService, AzureQueueEventWriteService>("storage");
|
||||||
|
|
||||||
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) &&
|
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) &&
|
||||||
CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.TopicName))
|
CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.TopicName))
|
||||||
{
|
{
|
||||||
services.AddSingleton<IEventWriteService, AzureServiceBusEventWriteService>();
|
services.AddKeyedSingleton<IEventWriteService, AzureServiceBusEventWriteService>("broadcast");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
services.AddSingleton<IEventWriteService, AzureQueueEventWriteService>();
|
services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (globalSettings.SelfHosted)
|
else if (globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
|
services.AddKeyedSingleton<IEventWriteService, RepositoryEventWriteService>("storage");
|
||||||
|
|
||||||
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HostName) &&
|
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HostName) &&
|
||||||
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Username) &&
|
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Username) &&
|
||||||
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Password) &&
|
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Password) &&
|
||||||
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.ExchangeName))
|
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.ExchangeName))
|
||||||
{
|
{
|
||||||
services.AddSingleton<IEventWriteService, RabbitMqEventWriteService>();
|
services.AddKeyedSingleton<IEventWriteService, RabbitMqEventWriteService>("broadcast");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
services.AddSingleton<IEventWriteService, RepositoryEventWriteService>();
|
services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
services.AddSingleton<IEventWriteService, NoopEventWriteService>();
|
services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("storage");
|
||||||
|
services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
|
||||||
}
|
}
|
||||||
|
services.AddScoped<IEventWriteService>(sp =>
|
||||||
|
{
|
||||||
|
var featureService = sp.GetRequiredService<IFeatureService>();
|
||||||
|
var key = featureService.IsEnabled(FeatureFlagKeys.EventBasedOrganizationIntegrations)
|
||||||
|
? "broadcast" : "storage";
|
||||||
|
return sp.GetRequiredKeyedService<IEventWriteService>(key);
|
||||||
|
});
|
||||||
|
|
||||||
if (CoreHelpers.SettingHasValue(globalSettings.Attachment.ConnectionString))
|
if (CoreHelpers.SettingHasValue(globalSettings.Attachment.ConnectionString))
|
||||||
{
|
{
|
||||||
|
@ -19,5 +19,19 @@ BEGIN
|
|||||||
FROM [dbo].[OrganizationSponsorship]
|
FROM [dbo].[OrganizationSponsorship]
|
||||||
WHERE SponsoringOrganizationId = @OrganizationId
|
WHERE SponsoringOrganizationId = @OrganizationId
|
||||||
AND IsAdminInitiated = 1
|
AND IsAdminInitiated = 1
|
||||||
|
AND (
|
||||||
|
-- Not marked for deletion - always count
|
||||||
|
(ToDelete = 0)
|
||||||
|
OR
|
||||||
|
-- Marked for deletion but has a valid until date in the future (RevokeWhenExpired status)
|
||||||
|
(ToDelete = 1 AND ValidUntil IS NOT NULL AND ValidUntil > GETUTCDATE())
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
-- SENT status: When SponsoredOrganizationId is null
|
||||||
|
SponsoredOrganizationId IS NULL
|
||||||
|
OR
|
||||||
|
-- ACCEPTED status: When SponsoredOrganizationId is not null and ValidUntil is null or in the future
|
||||||
|
(SponsoredOrganizationId IS NOT NULL AND (ValidUntil IS NULL OR ValidUntil > GETUTCDATE()))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
END
|
END
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text.Json;
|
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@ -12,22 +9,6 @@ namespace Bit.Core.Test.Models.Business;
|
|||||||
|
|
||||||
public class OrganizationLicenseTests
|
public class OrganizationLicenseTests
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Verifies that when the license file is loaded from disk using the current OrganizationLicense class,
|
|
||||||
/// its hash does not change.
|
|
||||||
/// This guards against the risk that properties added in later versions are accidentally included in the hash,
|
|
||||||
/// or that a property is added without incrementing the version number.
|
|
||||||
/// </summary>
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(OrganizationLicense.CurrentLicenseFileVersion)] // Previous version (this property is 1 behind)
|
|
||||||
[BitAutoData(OrganizationLicense.CurrentLicenseFileVersion + 1)] // Current version
|
|
||||||
public void OrganizationLicense_LoadFromDisk_HashDoesNotChange(int licenseVersion)
|
|
||||||
{
|
|
||||||
var license = OrganizationLicenseFileFixtures.GetVersion(licenseVersion);
|
|
||||||
|
|
||||||
// Compare the hash loaded from the json to the hash generated by the current class
|
|
||||||
Assert.Equal(Convert.FromBase64String(license.Hash), license.ComputeHash());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Verifies that when the license file is loaded from disk using the current OrganizationLicense class,
|
/// Verifies that when the license file is loaded from disk using the current OrganizationLicense class,
|
||||||
@ -52,22 +33,4 @@ public class OrganizationLicenseTests
|
|||||||
});
|
});
|
||||||
Assert.True(license.VerifyData(organization, claimsPrincipal, globalSettings));
|
Assert.True(license.VerifyData(organization, claimsPrincipal, globalSettings));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper used to generate a new json string to be added in OrganizationLicenseFileFixtures.
|
|
||||||
/// Uncomment [Fact], run the test and copy the value of the `result` variable into OrganizationLicenseFileFixtures,
|
|
||||||
/// following the instructions in that class.
|
|
||||||
/// </summary>
|
|
||||||
// [Fact]
|
|
||||||
private void GenerateLicenseFileJsonString()
|
|
||||||
{
|
|
||||||
var organization = OrganizationLicenseFileFixtures.OrganizationFactory();
|
|
||||||
var licensingService = Substitute.For<ILicensingService>();
|
|
||||||
var installationId = new Guid(OrganizationLicenseFileFixtures.InstallationId);
|
|
||||||
|
|
||||||
var license = new OrganizationLicense(organization, null, installationId, licensingService);
|
|
||||||
|
|
||||||
var result = JsonSerializer.Serialize(license, JsonHelpers.Indented).Replace("\"", "'");
|
|
||||||
// Put a break after this line, then copy and paste the value of `result` into OrganizationLicenseFileFixtures
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
IF OBJECT_ID('[dbo].[OrganizationUser_ReadOccupiedSeatCountByOrganizationId]') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
DROP PROCEDURE [dbo].[OrganizationUser_ReadOccupiedSeatCountByOrganizationId]
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE PROCEDURE [dbo].[OrganizationUser_ReadOccupiedSeatCountByOrganizationId]
|
||||||
|
@OrganizationId UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
(
|
||||||
|
SELECT COUNT(1)
|
||||||
|
FROM [dbo].[OrganizationUserView]
|
||||||
|
WHERE OrganizationId = @OrganizationId
|
||||||
|
AND Status >= 0 --Invited
|
||||||
|
) +
|
||||||
|
(
|
||||||
|
SELECT COUNT(1)
|
||||||
|
FROM [dbo].[OrganizationSponsorship]
|
||||||
|
WHERE SponsoringOrganizationId = @OrganizationId
|
||||||
|
AND IsAdminInitiated = 1
|
||||||
|
AND (
|
||||||
|
-- Not marked for deletion - always count
|
||||||
|
(ToDelete = 0)
|
||||||
|
OR
|
||||||
|
-- Marked for deletion but has a valid until date in the future (RevokeWhenExpired status)
|
||||||
|
(ToDelete = 1 AND ValidUntil IS NOT NULL AND ValidUntil > GETUTCDATE())
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
-- SENT status: When SponsoredOrganizationId is null
|
||||||
|
SponsoredOrganizationId IS NULL
|
||||||
|
OR
|
||||||
|
-- ACCEPTED status: When SponsoredOrganizationId is not null and ValidUntil is null or in the future
|
||||||
|
(SponsoredOrganizationId IS NOT NULL AND (ValidUntil IS NULL OR ValidUntil > GETUTCDATE()))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
END
|
||||||
|
GO
|
@ -10,7 +10,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="[8.0.8]">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="[8.0.8]">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="[8.0.8]">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="[8.0.8]">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user