1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-17 00:03:17 -05:00

Merge branch 'main' into user-service-test-tweaks

This commit is contained in:
Thomas Rittson 2025-06-11 15:26:18 +10:00 committed by GitHub
commit 61390c0553
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
283 changed files with 10756 additions and 1035 deletions

View File

@ -3,7 +3,7 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"swashbuckle.aspnetcore.cli": { "swashbuckle.aspnetcore.cli": {
"version": "7.2.0", "version": "7.3.2",
"commands": ["swagger"] "commands": ["swagger"]
}, },
"dotnet-ef": { "dotnet-ef": {

View File

@ -598,7 +598,7 @@ jobs:
uses: bitwarden/gh-actions/.github/workflows/_ephemeral_environment_manager.yml@main uses: bitwarden/gh-actions/.github/workflows/_ephemeral_environment_manager.yml@main
with: with:
project: server project: server
pull_request_number: ${{ github.event.number }} pull_request_number: ${{ github.event.number || 0 }}
secrets: inherit secrets: inherit
permissions: read-all permissions: read-all

View File

@ -550,6 +550,15 @@ public class ProviderBillingService(
[ [
new CustomerTaxIdDataOptions { Type = taxIdType, Value = taxInfo.TaxIdNumber } new CustomerTaxIdDataOptions { Type = taxIdType, Value = taxInfo.TaxIdNumber }
]; ];
if (taxIdType == StripeConstants.TaxIdType.SpanishNIF)
{
options.TaxIdData.Add(new CustomerTaxIdDataOptions
{
Type = StripeConstants.TaxIdType.EUVAT,
Value = $"ES{taxInfo.TaxIdNumber}"
});
}
} }
if (!string.IsNullOrEmpty(provider.DiscountId)) if (!string.IsNullOrEmpty(provider.DiscountId))

View File

@ -11,7 +11,7 @@ $corsRules = (@{
AllowedMethods = @("Get", "PUT"); AllowedMethods = @("Get", "PUT");
}); });
$containers = "attachments", "sendfiles", "misc"; $containers = "attachments", "sendfiles", "misc";
$queues = "event", "notifications", "reference-events", "mail"; $queues = "event", "notifications", "mail";
$tables = "event", "metadata", "installationdevice"; $tables = "event", "metadata", "installationdevice";
# End configuration # End configuration

View File

@ -242,10 +242,32 @@ public class OrganizationsController : Controller
Seats = organization.Seats Seats = organization.Seats
}; };
if (model.PlanType.HasValue)
{
var freePlan = await _pricingClient.GetPlanOrThrow(model.PlanType.Value);
var isDowngradingToFree = organization.PlanType != PlanType.Free && model.PlanType.Value == PlanType.Free;
if (isDowngradingToFree)
{
if (model.Seats.HasValue && model.Seats.Value > freePlan.PasswordManager.MaxSeats)
{
TempData["Error"] = $"Organizations with more than {freePlan.PasswordManager.MaxSeats} seats cannot be downgraded to the Free plan";
return RedirectToAction("Edit", new { id });
}
if (model.MaxCollections > freePlan.PasswordManager.MaxCollections)
{
TempData["Error"] = $"Organizations with more than {freePlan.PasswordManager.MaxCollections} collections cannot be downgraded to the Free plan. Your organization currently has {organization.MaxCollections} collections.";
return RedirectToAction("Edit", new { id });
}
model.MaxStorageGb = null;
model.ExpirationDate = null;
model.Enabled = true;
}
}
UpdateOrganization(organization, model); UpdateOrganization(organization, model);
var plan = await _pricingClient.GetPlanOrThrow(organization.PlanType); var plan = await _pricingClient.GetPlanOrThrow(organization.PlanType);
if (organization.UseSecretsManager && !plan.SupportsSecretsManager) if (organization.UseSecretsManager && !plan.SupportsSecretsManager)
{ {
TempData["Error"] = "Plan does not support Secrets Manager"; TempData["Error"] = "Plan does not support Secrets Manager";

View File

@ -34,7 +34,7 @@
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="8.0.2" /> <PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="8.0.2" />
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="8.0.1" /> <PackageReference Include="AspNetCore.HealthChecks.Uris" Version="8.0.1" />
<PackageReference Include="Azure.Messaging.EventGrid" Version="4.25.0" /> <PackageReference Include="Azure.Messaging.EventGrid" Version="4.25.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="7.3.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -4,6 +4,7 @@ using Bit.Api.AdminConsole.Models.Request.Organizations;
using Bit.Api.Billing.Models.Requests; using Bit.Api.Billing.Models.Requests;
using Bit.Api.Billing.Models.Responses; using Bit.Api.Billing.Models.Responses;
using Bit.Api.Billing.Queries.Organizations; using Bit.Api.Billing.Queries.Organizations;
using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Models; using Bit.Core.Billing.Models;
using Bit.Core.Billing.Models.Sales; using Bit.Core.Billing.Models.Sales;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
@ -280,17 +281,36 @@ public class OrganizationBillingController(
} }
var organization = await organizationRepository.GetByIdAsync(organizationId); var organization = await organizationRepository.GetByIdAsync(organizationId);
if (organization == null) if (organization == null)
{ {
return Error.NotFound(); return Error.NotFound();
} }
var existingPlan = organization.PlanType;
var organizationSignup = model.ToOrganizationSignup(user); var organizationSignup = model.ToOrganizationSignup(user);
var sale = OrganizationSale.From(organization, organizationSignup); var sale = OrganizationSale.From(organization, organizationSignup);
var plan = await pricingClient.GetPlanOrThrow(model.PlanType); var plan = await pricingClient.GetPlanOrThrow(model.PlanType);
sale.Organization.PlanType = plan.Type; sale.Organization.PlanType = plan.Type;
sale.Organization.Plan = plan.Name; sale.Organization.Plan = plan.Name;
sale.SubscriptionSetup.SkipTrial = true; sale.SubscriptionSetup.SkipTrial = true;
if (existingPlan == PlanType.Free && organization.GatewaySubscriptionId is not null)
{
sale.Organization.UseTotp = plan.HasTotp;
sale.Organization.UseGroups = plan.HasGroups;
sale.Organization.UseDirectory = plan.HasDirectory;
sale.Organization.SelfHost = plan.HasSelfHost;
sale.Organization.UsersGetPremium = plan.UsersGetPremium;
sale.Organization.UseEvents = plan.HasEvents;
sale.Organization.Use2fa = plan.Has2fa;
sale.Organization.UseApi = plan.HasApi;
sale.Organization.UsePolicies = plan.HasPolicies;
sale.Organization.UseSso = plan.HasSso;
sale.Organization.UseResetPassword = plan.HasResetPassword;
sale.Organization.UseKeyConnector = plan.HasKeyConnector;
sale.Organization.UseScim = plan.HasScim;
sale.Organization.UseCustomPermissions = plan.HasCustomPermissions;
sale.Organization.UseOrganizationDomains = plan.HasOrganizationDomains;
sale.Organization.MaxCollections = plan.PasswordManager.MaxCollections;
}
if (organizationSignup.PaymentMethodType == null || string.IsNullOrEmpty(organizationSignup.PaymentToken)) if (organizationSignup.PaymentMethodType == null || string.IsNullOrEmpty(organizationSignup.PaymentToken))
{ {

View File

@ -8,7 +8,7 @@ using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Tools.Controllers; namespace Bit.Api.Dirt.Controllers;
[Route("hibp")] [Route("hibp")]
[Authorize("Application")] [Authorize("Application")]

View File

@ -1,16 +1,16 @@
using Bit.Api.Tools.Models; using Bit.Api.Dirt.Models;
using Bit.Api.Tools.Models.Response; using Bit.Api.Dirt.Models.Response;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Dirt.Reports.Entities;
using Bit.Core.Dirt.Reports.Models.Data;
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
using Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces;
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Tools.Entities;
using Bit.Core.Tools.Models.Data;
using Bit.Core.Tools.ReportFeatures.Interfaces;
using Bit.Core.Tools.ReportFeatures.OrganizationReportMembers.Interfaces;
using Bit.Core.Tools.ReportFeatures.Requests;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Tools.Controllers; namespace Bit.Api.Dirt.Controllers;
[Route("reports")] [Route("reports")]
[Authorize("Application")] [Authorize("Application")]

View File

@ -1,4 +1,4 @@
namespace Bit.Api.Tools.Models; namespace Bit.Api.Dirt.Models;
public class PasswordHealthReportApplicationModel public class PasswordHealthReportApplicationModel
{ {

View File

@ -1,6 +1,6 @@
using Bit.Core.Tools.Models.Data; using Bit.Core.Dirt.Reports.Models.Data;
namespace Bit.Api.Tools.Models.Response; namespace Bit.Api.Dirt.Models.Response;
/// <summary> /// <summary>
/// Contains the collections and group collections a user has access to including /// Contains the collections and group collections a user has access to including

View File

@ -1,6 +1,6 @@
using Bit.Core.Tools.Models.Data; using Bit.Core.Dirt.Reports.Models.Data;
namespace Bit.Api.Tools.Models.Response; namespace Bit.Api.Dirt.Models.Response;
public class MemberCipherDetailsResponseModel public class MemberCipherDetailsResponseModel
{ {

View File

@ -31,8 +31,8 @@ using Bit.Api.Billing;
using Bit.Core.Auth.Models.Data; using Bit.Core.Auth.Models.Data;
using Bit.Core.Auth.Identity.TokenProviders; using Bit.Core.Auth.Identity.TokenProviders;
using Bit.Core.Tools.ImportFeatures; using Bit.Core.Tools.ImportFeatures;
using Bit.Core.Tools.ReportFeatures;
using Bit.Core.Auth.Models.Api.Request; using Bit.Core.Auth.Models.Api.Request;
using Bit.Core.Dirt.Reports.ReportFeatures;
using Bit.Core.Tools.SendFeatures; using Bit.Core.Tools.SendFeatures;
#if !OSS #if !OSS

View File

@ -42,7 +42,6 @@ public class CiphersController : Controller
private readonly ICurrentContext _currentContext; private readonly ICurrentContext _currentContext;
private readonly ILogger<CiphersController> _logger; private readonly ILogger<CiphersController> _logger;
private readonly GlobalSettings _globalSettings; private readonly GlobalSettings _globalSettings;
private readonly IFeatureService _featureService;
private readonly IOrganizationCiphersQuery _organizationCiphersQuery; private readonly IOrganizationCiphersQuery _organizationCiphersQuery;
private readonly IApplicationCacheService _applicationCacheService; private readonly IApplicationCacheService _applicationCacheService;
private readonly ICollectionRepository _collectionRepository; private readonly ICollectionRepository _collectionRepository;
@ -57,7 +56,6 @@ public class CiphersController : Controller
ICurrentContext currentContext, ICurrentContext currentContext,
ILogger<CiphersController> logger, ILogger<CiphersController> logger,
GlobalSettings globalSettings, GlobalSettings globalSettings,
IFeatureService featureService,
IOrganizationCiphersQuery organizationCiphersQuery, IOrganizationCiphersQuery organizationCiphersQuery,
IApplicationCacheService applicationCacheService, IApplicationCacheService applicationCacheService,
ICollectionRepository collectionRepository) ICollectionRepository collectionRepository)
@ -71,7 +69,6 @@ public class CiphersController : Controller
_currentContext = currentContext; _currentContext = currentContext;
_logger = logger; _logger = logger;
_globalSettings = globalSettings; _globalSettings = globalSettings;
_featureService = featureService;
_organizationCiphersQuery = organizationCiphersQuery; _organizationCiphersQuery = organizationCiphersQuery;
_applicationCacheService = applicationCacheService; _applicationCacheService = applicationCacheService;
_collectionRepository = collectionRepository; _collectionRepository = collectionRepository;
@ -375,11 +372,6 @@ public class CiphersController : Controller
private async Task<bool> CanDeleteOrRestoreCipherAsAdminAsync(Guid organizationId, IEnumerable<Guid> cipherIds) private async Task<bool> CanDeleteOrRestoreCipherAsAdminAsync(Guid organizationId, IEnumerable<Guid> cipherIds)
{ {
if (!_featureService.IsEnabled(FeatureFlagKeys.LimitItemDeletion))
{
return await CanEditCipherAsAdminAsync(organizationId, cipherIds);
}
var org = _currentContext.GetOrganization(organizationId); var org = _currentContext.GetOrganization(organizationId);
// If we're not an "admin" or if we're a provider user we don't need to check the ciphers // If we're not an "admin" or if we're a provider user we don't need to check the ciphers

View File

@ -1,9 +1,7 @@
using Bit.Api.Models.Response; using Bit.Api.Models.Response;
using Bit.Api.Vault.Models.Request; using Bit.Api.Vault.Models.Request;
using Bit.Api.Vault.Models.Response; using Bit.Api.Vault.Models.Response;
using Bit.Core;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities;
using Bit.Core.Vault.Commands.Interfaces; using Bit.Core.Vault.Commands.Interfaces;
using Bit.Core.Vault.Entities; using Bit.Core.Vault.Entities;
using Bit.Core.Vault.Enums; using Bit.Core.Vault.Enums;
@ -15,7 +13,6 @@ namespace Bit.Api.Vault.Controllers;
[Route("tasks")] [Route("tasks")]
[Authorize("Application")] [Authorize("Application")]
[RequireFeature(FeatureFlagKeys.SecurityTasks)]
public class SecurityTaskController : Controller public class SecurityTaskController : Controller
{ {
private readonly IUserService _userService; private readonly IUserService _userService;

View File

@ -10,7 +10,7 @@
<ProjectReference Include="..\Core\Core.csproj" /> <ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="7.3.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -17,6 +17,7 @@ public enum PolicyType : byte
AutomaticAppLogIn = 12, AutomaticAppLogIn = 12,
FreeFamiliesSponsorshipPolicy = 13, FreeFamiliesSponsorshipPolicy = 13,
RemoveUnlockWithPin = 14, RemoveUnlockWithPin = 14,
RestrictedItemTypesPolicy = 15,
} }
public static class PolicyTypeExtensions public static class PolicyTypeExtensions
@ -43,7 +44,8 @@ public static class PolicyTypeExtensions
PolicyType.ActivateAutofill => "Active auto-fill", PolicyType.ActivateAutofill => "Active auto-fill",
PolicyType.AutomaticAppLogIn => "Automatically log in users for allowed applications", PolicyType.AutomaticAppLogIn => "Automatically log in users for allowed applications",
PolicyType.FreeFamiliesSponsorshipPolicy => "Remove Free Bitwarden Families sponsorship", PolicyType.FreeFamiliesSponsorshipPolicy => "Remove Free Bitwarden Families sponsorship",
PolicyType.RemoveUnlockWithPin => "Remove unlock with PIN" PolicyType.RemoveUnlockWithPin => "Remove unlock with PIN",
PolicyType.RestrictedItemTypesPolicy => "Restricted item types",
}; };
} }
} }

View File

@ -96,6 +96,12 @@ public static class StripeConstants
public const string Reverse = "reverse"; public const string Reverse = "reverse";
} }
public static class TaxIdType
{
public const string EUVAT = "eu_vat";
public const string SpanishNIF = "es_cif";
}
public static class ValidateTaxLocationTiming public static class ValidateTaxLocationTiming
{ {
public const string Deferred = "deferred"; public const string Deferred = "deferred";

View File

@ -31,6 +31,7 @@ public record PlanAdapter : Plan
HasScim = HasFeature("scim"); HasScim = HasFeature("scim");
HasResetPassword = HasFeature("resetPassword"); HasResetPassword = HasFeature("resetPassword");
UsersGetPremium = HasFeature("usersGetPremium"); UsersGetPremium = HasFeature("usersGetPremium");
HasCustomPermissions = HasFeature("customPermissions");
UpgradeSortOrder = plan.AdditionalData.TryGetValue("upgradeSortOrder", out var upgradeSortOrder) UpgradeSortOrder = plan.AdditionalData.TryGetValue("upgradeSortOrder", out var upgradeSortOrder)
? int.Parse(upgradeSortOrder) ? int.Parse(upgradeSortOrder)
: 0; : 0;
@ -141,6 +142,7 @@ public record PlanAdapter : Plan
var stripeSeatPlanId = GetStripeSeatPlanId(seats); var stripeSeatPlanId = GetStripeSeatPlanId(seats);
var hasAdditionalSeatsOption = seats.IsScalable; var hasAdditionalSeatsOption = seats.IsScalable;
var seatPrice = GetSeatPrice(seats); var seatPrice = GetSeatPrice(seats);
var baseSeats = GetBaseSeats(seats);
var maxSeats = GetMaxSeats(seats); var maxSeats = GetMaxSeats(seats);
var allowSeatAutoscale = seats.IsScalable; var allowSeatAutoscale = seats.IsScalable;
var maxProjects = plan.AdditionalData.TryGetValue("secretsManager.maxProjects", out var value) ? short.Parse(value) : 0; var maxProjects = plan.AdditionalData.TryGetValue("secretsManager.maxProjects", out var value) ? short.Parse(value) : 0;
@ -156,6 +158,7 @@ public record PlanAdapter : Plan
StripeSeatPlanId = stripeSeatPlanId, StripeSeatPlanId = stripeSeatPlanId,
HasAdditionalSeatsOption = hasAdditionalSeatsOption, HasAdditionalSeatsOption = hasAdditionalSeatsOption,
SeatPrice = seatPrice, SeatPrice = seatPrice,
BaseSeats = baseSeats,
MaxSeats = maxSeats, MaxSeats = maxSeats,
AllowSeatAutoscale = allowSeatAutoscale, AllowSeatAutoscale = allowSeatAutoscale,
MaxProjects = maxProjects MaxProjects = maxProjects
@ -168,8 +171,16 @@ public record PlanAdapter : Plan
private static decimal GetBasePrice(PurchasableDTO purchasable) private static decimal GetBasePrice(PurchasableDTO purchasable)
=> purchasable.FromPackaged(x => x.Price); => purchasable.FromPackaged(x => x.Price);
private static int GetBaseSeats(FreeOrScalableDTO freeOrScalable)
=> freeOrScalable.Match(
free => free.Quantity,
scalable => scalable.Provided);
private static int GetBaseSeats(PurchasableDTO purchasable) private static int GetBaseSeats(PurchasableDTO purchasable)
=> purchasable.FromPackaged(x => x.Quantity); => purchasable.Match(
free => free.Quantity,
packaged => packaged.Quantity,
scalable => scalable.Provided);
private static short GetBaseServiceAccount(FreeOrScalableDTO freeOrScalable) private static short GetBaseServiceAccount(FreeOrScalableDTO freeOrScalable)
=> freeOrScalable.Match( => freeOrScalable.Match(

View File

@ -78,13 +78,14 @@ public class OrganizationBillingService(
var isEligibleForSelfHost = await IsEligibleForSelfHostAsync(organization); var isEligibleForSelfHost = await IsEligibleForSelfHostAsync(organization);
var isManaged = organization.Status == OrganizationStatusType.Managed; var isManaged = organization.Status == OrganizationStatusType.Managed;
var orgOccupiedSeats = await organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
if (string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId)) if (string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId))
{ {
return OrganizationMetadata.Default with return OrganizationMetadata.Default with
{ {
IsEligibleForSelfHost = isEligibleForSelfHost, IsEligibleForSelfHost = isEligibleForSelfHost,
IsManaged = isManaged IsManaged = isManaged,
OrganizationOccupiedSeats = orgOccupiedSeats
}; };
} }
@ -108,8 +109,6 @@ public class OrganizationBillingService(
? await stripeAdapter.InvoiceGetAsync(subscription.LatestInvoiceId, new InvoiceGetOptions()) ? await stripeAdapter.InvoiceGetAsync(subscription.LatestInvoiceId, new InvoiceGetOptions())
: null; : null;
var orgOccupiedSeats = await organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
return new OrganizationMetadata( return new OrganizationMetadata(
isEligibleForSelfHost, isEligibleForSelfHost,
isManaged, isManaged,
@ -248,12 +247,23 @@ public class OrganizationBillingService(
organization.Id, organization.Id,
customerSetup.TaxInformation.Country, customerSetup.TaxInformation.Country,
customerSetup.TaxInformation.TaxId); customerSetup.TaxInformation.TaxId);
throw new BadRequestException("billingTaxIdTypeInferenceError");
} }
customerCreateOptions.TaxIdData = customerCreateOptions.TaxIdData =
[ [
new() { Type = taxIdType, Value = customerSetup.TaxInformation.TaxId } new() { Type = taxIdType, Value = customerSetup.TaxInformation.TaxId }
]; ];
if (taxIdType == StripeConstants.TaxIdType.SpanishNIF)
{
customerCreateOptions.TaxIdData.Add(new CustomerTaxIdDataOptions
{
Type = StripeConstants.TaxIdType.EUVAT,
Value = $"ES{customerSetup.TaxInformation.TaxId}"
});
}
} }
var (paymentMethodType, paymentMethodToken) = customerSetup.TokenizedPaymentSource; var (paymentMethodType, paymentMethodToken) = customerSetup.TokenizedPaymentSource;
@ -420,7 +430,7 @@ public class OrganizationBillingService(
var setNonUSBusinessUseToReverseCharge = var setNonUSBusinessUseToReverseCharge =
featureService.IsEnabled(FeatureFlagKeys.PM21092_SetNonUSBusinessUseToReverseCharge); featureService.IsEnabled(FeatureFlagKeys.PM21092_SetNonUSBusinessUseToReverseCharge);
if (setNonUSBusinessUseToReverseCharge) if (setNonUSBusinessUseToReverseCharge && customer.HasBillingLocation())
{ {
subscriptionCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true }; subscriptionCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true };
} }

View File

@ -648,6 +648,12 @@ public class SubscriberService(
{ {
await stripeAdapter.TaxIdCreateAsync(customer.Id, await stripeAdapter.TaxIdCreateAsync(customer.Id,
new TaxIdCreateOptions { Type = taxIdType, Value = taxInformation.TaxId }); new TaxIdCreateOptions { Type = taxIdType, Value = taxInformation.TaxId });
if (taxIdType == StripeConstants.TaxIdType.SpanishNIF)
{
await stripeAdapter.TaxIdCreateAsync(customer.Id,
new TaxIdCreateOptions { Type = StripeConstants.TaxIdType.EUVAT, Value = $"ES{taxInformation.TaxId}" });
}
} }
catch (StripeException e) catch (StripeException e)
{ {

View File

@ -80,6 +80,15 @@ public class PreviewTaxAmountCommand(
Value = taxInformation.TaxId Value = taxInformation.TaxId
} }
]; ];
if (taxIdType == StripeConstants.TaxIdType.SpanishNIF)
{
options.CustomerDetails.TaxIds.Add(new InvoiceCustomerDetailsTaxIdOptions
{
Type = StripeConstants.TaxIdType.EUVAT,
Value = $"ES{parameters.TaxInformation.TaxId}"
});
}
} }
if (planType.GetProductTier() == ProductTierType.Families) if (planType.GetProductTier() == ProductTierType.Families)

View File

@ -181,6 +181,8 @@ public static class FeatureFlagKeys
public const string EnablePMFlightRecorder = "enable-pm-flight-recorder"; public const string EnablePMFlightRecorder = "enable-pm-flight-recorder";
public const string MobileErrorReporting = "mobile-error-reporting"; public const string MobileErrorReporting = "mobile-error-reporting";
public const string AndroidChromeAutofill = "android-chrome-autofill"; public const string AndroidChromeAutofill = "android-chrome-autofill";
public const string EnablePMPreloginSettings = "enable-pm-prelogin-settings";
public const string AppIntents = "app-intents";
/* Platform Team */ /* Platform Team */
public const string PersistPopupView = "persist-popup-view"; public const string PersistPopupView = "persist-popup-view";

View File

@ -1,9 +1,9 @@
using Bit.Core.Entities; #nullable enable
using Bit.Core.Entities;
using Bit.Core.Utilities; using Bit.Core.Utilities;
#nullable enable namespace Bit.Core.Dirt.Reports.Entities;
namespace Bit.Core.Tools.Entities;
public class PasswordHealthReportApplication : ITableObject<Guid>, IRevisable public class PasswordHealthReportApplication : ITableObject<Guid>, IRevisable
{ {

View File

@ -1,4 +1,4 @@
namespace Bit.Core.Tools.Models.Data; namespace Bit.Core.Dirt.Reports.Models.Data;
public class MemberAccessDetails public class MemberAccessDetails
{ {

View File

@ -1,11 +1,11 @@
using Bit.Core.Exceptions; using Bit.Core.Dirt.Reports.Entities;
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
using Bit.Core.Dirt.Reports.Repositories;
using Bit.Core.Exceptions;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Tools.Entities;
using Bit.Core.Tools.ReportFeatures.Interfaces;
using Bit.Core.Tools.ReportFeatures.Requests;
using Bit.Core.Tools.Repositories;
namespace Bit.Core.Tools.ReportFeatures; namespace Bit.Core.Dirt.Reports.ReportFeatures;
public class AddPasswordHealthReportApplicationCommand : IAddPasswordHealthReportApplicationCommand public class AddPasswordHealthReportApplicationCommand : IAddPasswordHealthReportApplicationCommand
{ {

View File

@ -1,9 +1,9 @@
using Bit.Core.Exceptions; using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
using Bit.Core.Tools.ReportFeatures.Interfaces; using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
using Bit.Core.Tools.ReportFeatures.Requests; using Bit.Core.Dirt.Reports.Repositories;
using Bit.Core.Tools.Repositories; using Bit.Core.Exceptions;
namespace Bit.Core.Tools.ReportFeatures; namespace Bit.Core.Dirt.Reports.ReportFeatures;
public class DropPasswordHealthReportApplicationCommand : IDropPasswordHealthReportApplicationCommand public class DropPasswordHealthReportApplicationCommand : IDropPasswordHealthReportApplicationCommand
{ {

View File

@ -1,9 +1,9 @@
using Bit.Core.Exceptions; using Bit.Core.Dirt.Reports.Entities;
using Bit.Core.Tools.Entities; using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
using Bit.Core.Tools.ReportFeatures.Interfaces; using Bit.Core.Dirt.Reports.Repositories;
using Bit.Core.Tools.Repositories; using Bit.Core.Exceptions;
namespace Bit.Core.Tools.ReportFeatures; namespace Bit.Core.Dirt.Reports.ReportFeatures;
public class GetPasswordHealthReportApplicationQuery : IGetPasswordHealthReportApplicationQuery public class GetPasswordHealthReportApplicationQuery : IGetPasswordHealthReportApplicationQuery
{ {

View File

@ -1,7 +1,7 @@
using Bit.Core.Tools.Entities; using Bit.Core.Dirt.Reports.Entities;
using Bit.Core.Tools.ReportFeatures.Requests; using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
namespace Bit.Core.Tools.ReportFeatures.Interfaces; namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
public interface IAddPasswordHealthReportApplicationCommand public interface IAddPasswordHealthReportApplicationCommand
{ {

View File

@ -1,6 +1,6 @@
using Bit.Core.Tools.ReportFeatures.Requests; using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
namespace Bit.Core.Tools.ReportFeatures.Interfaces; namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
public interface IDropPasswordHealthReportApplicationCommand public interface IDropPasswordHealthReportApplicationCommand
{ {

View File

@ -1,6 +1,6 @@
using Bit.Core.Tools.Entities; using Bit.Core.Dirt.Reports.Entities;
namespace Bit.Core.Tools.ReportFeatures.Interfaces; namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
public interface IGetPasswordHealthReportApplicationQuery public interface IGetPasswordHealthReportApplicationQuery
{ {

View File

@ -2,21 +2,21 @@
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
using Bit.Core.Dirt.Reports.Models.Data;
using Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces;
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations; using Bit.Core.Models.Data.Organizations;
using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Tools.Models.Data;
using Bit.Core.Tools.ReportFeatures.OrganizationReportMembers.Interfaces;
using Bit.Core.Tools.ReportFeatures.Requests;
using Bit.Core.Vault.Models.Data; using Bit.Core.Vault.Models.Data;
using Bit.Core.Vault.Queries; using Bit.Core.Vault.Queries;
using Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests; using Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
namespace Bit.Core.Tools.ReportFeatures; namespace Bit.Core.Dirt.Reports.ReportFeatures;
public class MemberAccessCipherDetailsQuery : IMemberAccessCipherDetailsQuery public class MemberAccessCipherDetailsQuery : IMemberAccessCipherDetailsQuery
{ {

View File

@ -1,7 +1,7 @@
using Bit.Core.Tools.Models.Data; using Bit.Core.Dirt.Reports.Models.Data;
using Bit.Core.Tools.ReportFeatures.Requests; using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
namespace Bit.Core.Tools.ReportFeatures.OrganizationReportMembers.Interfaces; namespace Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces;
public interface IMemberAccessCipherDetailsQuery public interface IMemberAccessCipherDetailsQuery
{ {

View File

@ -1,8 +1,8 @@
using Bit.Core.Tools.ReportFeatures.Interfaces; using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
using Bit.Core.Tools.ReportFeatures.OrganizationReportMembers.Interfaces; using Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Bit.Core.Tools.ReportFeatures; namespace Bit.Core.Dirt.Reports.ReportFeatures;
public static class ReportingServiceCollectionExtensions public static class ReportingServiceCollectionExtensions
{ {

View File

@ -1,4 +1,4 @@
namespace Bit.Core.Tools.ReportFeatures.Requests; namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
public class AddPasswordHealthReportApplicationRequest public class AddPasswordHealthReportApplicationRequest
{ {

View File

@ -1,4 +1,4 @@
namespace Bit.Core.Tools.ReportFeatures.Requests; namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
public class DropPasswordHealthReportApplicationRequest public class DropPasswordHealthReportApplicationRequest
{ {

View File

@ -1,4 +1,4 @@
namespace Bit.Core.Tools.ReportFeatures.Requests; namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
public class MemberAccessCipherDetailsRequest public class MemberAccessCipherDetailsRequest
{ {

View File

@ -1,7 +1,7 @@
using Bit.Core.Repositories; using Bit.Core.Dirt.Reports.Entities;
using Bit.Core.Tools.Entities; using Bit.Core.Repositories;
namespace Bit.Core.Tools.Repositories; namespace Bit.Core.Dirt.Reports.Repositories;
public interface IPasswordHealthReportApplicationRepository : IRepository<PasswordHealthReportApplication, Guid> public interface IPasswordHealthReportApplicationRepository : IRepository<PasswordHealthReportApplication, Guid>
{ {

View File

@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using Bit.Core.Utilities; using Bit.Core.Utilities;
#nullable enable #nullable enable
@ -14,6 +15,8 @@ public class Collection : ITableObject<Guid>
public string? ExternalId { get; set; } public string? ExternalId { get; set; }
public DateTime CreationDate { get; set; } = DateTime.UtcNow; public DateTime CreationDate { get; set; } = DateTime.UtcNow;
public DateTime RevisionDate { get; set; } = DateTime.UtcNow; public DateTime RevisionDate { get; set; } = DateTime.UtcNow;
public CollectionType Type { get; set; } = CollectionType.SharedCollection;
public string? DefaultUserCollectionEmail { get; set; }
public void SetNewId() public void SetNewId()
{ {

View File

@ -0,0 +1,7 @@
namespace Bit.Core.Enums;
public enum CollectionType
{
SharedCollection = 0,
DefaultUserCollection = 1,
}

View File

@ -3,6 +3,8 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Bit.Core.Exceptions; namespace Bit.Core.Exceptions;
#nullable enable
public class BadRequestException : Exception public class BadRequestException : Exception
{ {
public BadRequestException() : base() public BadRequestException() : base()
@ -41,5 +43,5 @@ public class BadRequestException : Exception
} }
} }
public ModelStateDictionary ModelState { get; set; } public ModelStateDictionary? ModelState { get; set; }
} }

View File

@ -1,5 +1,7 @@
namespace Bit.Core.Exceptions; namespace Bit.Core.Exceptions;
#nullable enable
public class ConflictException : Exception public class ConflictException : Exception
{ {
public ConflictException() : base("Conflict.") { } public ConflictException() : base("Conflict.") { }

View File

@ -1,5 +1,7 @@
namespace Bit.Core.Exceptions; namespace Bit.Core.Exceptions;
#nullable enable
public class DnsQueryException : Exception public class DnsQueryException : Exception
{ {
public DnsQueryException(string message) public DnsQueryException(string message)

View File

@ -1,5 +1,7 @@
namespace Bit.Core.Exceptions; namespace Bit.Core.Exceptions;
#nullable enable
public class DomainClaimedException : Exception public class DomainClaimedException : Exception
{ {
public DomainClaimedException() public DomainClaimedException()

View File

@ -1,5 +1,7 @@
namespace Bit.Core.Exceptions; namespace Bit.Core.Exceptions;
#nullable enable
public class DomainVerifiedException : Exception public class DomainVerifiedException : Exception
{ {
public DomainVerifiedException() public DomainVerifiedException()

View File

@ -1,5 +1,7 @@
namespace Bit.Core.Exceptions; namespace Bit.Core.Exceptions;
#nullable enable
public class DuplicateDomainException : Exception public class DuplicateDomainException : Exception
{ {
public DuplicateDomainException() public DuplicateDomainException()

View File

@ -1,5 +1,7 @@
namespace Bit.Core.Exceptions; namespace Bit.Core.Exceptions;
#nullable enable
/// <summary> /// <summary>
/// Exception to throw when a requested feature is not yet enabled/available for the requesting context. /// Exception to throw when a requested feature is not yet enabled/available for the requesting context.
/// </summary> /// </summary>

View File

@ -1,8 +1,10 @@
namespace Bit.Core.Exceptions; namespace Bit.Core.Exceptions;
#nullable enable
public class GatewayException : Exception public class GatewayException : Exception
{ {
public GatewayException(string message, Exception innerException = null) public GatewayException(string message, Exception? innerException = null)
: base(message, innerException) : base(message, innerException)
{ } { }
} }

View File

@ -1,5 +1,7 @@
namespace Bit.Core.Exceptions; namespace Bit.Core.Exceptions;
#nullable enable
public class InvalidEmailException : Exception public class InvalidEmailException : Exception
{ {
public InvalidEmailException() public InvalidEmailException()

View File

@ -1,5 +1,7 @@
namespace Bit.Core.Exceptions; namespace Bit.Core.Exceptions;
#nullable enable
public class InvalidGatewayCustomerIdException : Exception public class InvalidGatewayCustomerIdException : Exception
{ {
public InvalidGatewayCustomerIdException() public InvalidGatewayCustomerIdException()

View File

@ -1,5 +1,7 @@
namespace Bit.Core.Exceptions; namespace Bit.Core.Exceptions;
#nullable enable
public class NotFoundException : Exception public class NotFoundException : Exception
{ {
public NotFoundException() : base() public NotFoundException() : base()

View File

@ -10,9 +10,11 @@ using Microsoft.Extensions.Logging;
namespace Bit.Core.HostedServices; namespace Bit.Core.HostedServices;
#nullable enable
public class ApplicationCacheHostedService : IHostedService, IDisposable public class ApplicationCacheHostedService : IHostedService, IDisposable
{ {
private readonly InMemoryServiceBusApplicationCacheService _applicationCacheService; private readonly InMemoryServiceBusApplicationCacheService? _applicationCacheService;
private readonly IOrganizationRepository _organizationRepository; private readonly IOrganizationRepository _organizationRepository;
protected readonly ILogger<ApplicationCacheHostedService> _logger; protected readonly ILogger<ApplicationCacheHostedService> _logger;
private readonly ServiceBusClient _serviceBusClient; private readonly ServiceBusClient _serviceBusClient;
@ -20,8 +22,8 @@ public class ApplicationCacheHostedService : IHostedService, IDisposable
private readonly ServiceBusAdministrationClient _serviceBusAdministrationClient; private readonly ServiceBusAdministrationClient _serviceBusAdministrationClient;
private readonly string _subName; private readonly string _subName;
private readonly string _topicName; private readonly string _topicName;
private CancellationTokenSource _cts; private CancellationTokenSource? _cts;
private Task _executingTask; private Task? _executingTask;
public ApplicationCacheHostedService( public ApplicationCacheHostedService(
@ -67,13 +69,17 @@ public class ApplicationCacheHostedService : IHostedService, IDisposable
{ {
await _subscriptionReceiver.CloseAsync(cancellationToken); await _subscriptionReceiver.CloseAsync(cancellationToken);
await _serviceBusClient.DisposeAsync(); await _serviceBusClient.DisposeAsync();
_cts.Cancel(); _cts?.Cancel();
try try
{ {
await _serviceBusAdministrationClient.DeleteSubscriptionAsync(_topicName, _subName, cancellationToken); await _serviceBusAdministrationClient.DeleteSubscriptionAsync(_topicName, _subName, cancellationToken);
} }
catch { } catch { }
await _executingTask;
if (_executingTask != null)
{
await _executingTask;
}
} }
public virtual void Dispose() public virtual void Dispose()

View File

@ -3,6 +3,8 @@ using Microsoft.Extensions.Hosting;
namespace Bit.Core.HostedServices; namespace Bit.Core.HostedServices;
#nullable enable
/// <summary> /// <summary>
/// A startup service that will seed the IP rate limiting stores with any values in the /// A startup service that will seed the IP rate limiting stores with any values in the
/// GlobalSettings configuration. /// GlobalSettings configuration.

View File

@ -3,6 +3,8 @@ using Quartz;
namespace Bit.Core.Jobs; namespace Bit.Core.Jobs;
#nullable enable
public abstract class BaseJob : IJob public abstract class BaseJob : IJob
{ {
protected readonly ILogger _logger; protected readonly ILogger _logger;

View File

@ -8,6 +8,8 @@ using Quartz.Impl.Matchers;
namespace Bit.Core.Jobs; namespace Bit.Core.Jobs;
#nullable enable
public abstract class BaseJobsHostedService : IHostedService, IDisposable public abstract class BaseJobsHostedService : IHostedService, IDisposable
{ {
private const int MaximumJobRetries = 10; private const int MaximumJobRetries = 10;
@ -16,7 +18,7 @@ public abstract class BaseJobsHostedService : IHostedService, IDisposable
private readonly ILogger<JobListener> _listenerLogger; private readonly ILogger<JobListener> _listenerLogger;
protected readonly ILogger _logger; protected readonly ILogger _logger;
private IScheduler _scheduler; private IScheduler? _scheduler;
protected GlobalSettings _globalSettings; protected GlobalSettings _globalSettings;
public BaseJobsHostedService( public BaseJobsHostedService(
@ -31,7 +33,7 @@ public abstract class BaseJobsHostedService : IHostedService, IDisposable
_globalSettings = globalSettings; _globalSettings = globalSettings;
} }
public IEnumerable<Tuple<Type, ITrigger>> Jobs { get; protected set; } public IEnumerable<Tuple<Type, ITrigger>>? Jobs { get; protected set; }
public virtual async Task StartAsync(CancellationToken cancellationToken) public virtual async Task StartAsync(CancellationToken cancellationToken)
{ {
@ -61,10 +63,19 @@ public abstract class BaseJobsHostedService : IHostedService, IDisposable
_scheduler.ListenerManager.AddJobListener(new JobListener(_listenerLogger), _scheduler.ListenerManager.AddJobListener(new JobListener(_listenerLogger),
GroupMatcher<JobKey>.AnyGroup()); GroupMatcher<JobKey>.AnyGroup());
await _scheduler.Start(cancellationToken); await _scheduler.Start(cancellationToken);
var jobKeys = new List<JobKey>();
var triggerKeys = new List<TriggerKey>();
if (Jobs != null) if (Jobs != null)
{ {
foreach (var (job, trigger) in Jobs) foreach (var (job, trigger) in Jobs)
{ {
jobKeys.Add(JobBuilder.Create(job)
.WithIdentity(job.FullName!)
.Build().Key);
triggerKeys.Add(trigger.Key);
for (var retry = 0; retry < MaximumJobRetries; retry++) for (var retry = 0; retry < MaximumJobRetries; retry++)
{ {
// There's a race condition when starting multiple containers simultaneously, retry until it succeeds.. // There's a race condition when starting multiple containers simultaneously, retry until it succeeds..
@ -77,7 +88,7 @@ public abstract class BaseJobsHostedService : IHostedService, IDisposable
} }
var jobDetail = JobBuilder.Create(job) var jobDetail = JobBuilder.Create(job)
.WithIdentity(job.FullName) .WithIdentity(job.FullName!)
.Build(); .Build();
var dupeJ = await _scheduler.GetJobDetail(jobDetail.Key); var dupeJ = await _scheduler.GetJobDetail(jobDetail.Key);
@ -106,13 +117,6 @@ public abstract class BaseJobsHostedService : IHostedService, IDisposable
// Delete old Jobs and Triggers // Delete old Jobs and Triggers
var existingJobKeys = await _scheduler.GetJobKeys(GroupMatcher<JobKey>.AnyGroup()); var existingJobKeys = await _scheduler.GetJobKeys(GroupMatcher<JobKey>.AnyGroup());
var jobKeys = Jobs.Select(j =>
{
var job = j.Item1;
return JobBuilder.Create(job)
.WithIdentity(job.FullName)
.Build().Key;
});
foreach (var key in existingJobKeys) foreach (var key in existingJobKeys)
{ {
@ -126,7 +130,6 @@ public abstract class BaseJobsHostedService : IHostedService, IDisposable
} }
var existingTriggerKeys = await _scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.AnyGroup()); var existingTriggerKeys = await _scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.AnyGroup());
var triggerKeys = Jobs.Select(j => j.Item2.Key);
foreach (var key in existingTriggerKeys) foreach (var key in existingTriggerKeys)
{ {
@ -142,7 +145,10 @@ public abstract class BaseJobsHostedService : IHostedService, IDisposable
public virtual async Task StopAsync(CancellationToken cancellationToken) public virtual async Task StopAsync(CancellationToken cancellationToken)
{ {
await _scheduler?.Shutdown(cancellationToken); if (_scheduler is not null)
{
await _scheduler.Shutdown(cancellationToken);
}
} }
public virtual void Dispose() public virtual void Dispose()

View File

@ -4,6 +4,8 @@ using Quartz.Spi;
namespace Bit.Core.Jobs; namespace Bit.Core.Jobs;
#nullable enable
public class JobFactory : IJobFactory public class JobFactory : IJobFactory
{ {
private readonly IServiceProvider _container; private readonly IServiceProvider _container;
@ -16,7 +18,7 @@ public class JobFactory : IJobFactory
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{ {
var scope = _container.CreateScope(); var scope = _container.CreateScope();
return scope.ServiceProvider.GetService(bundle.JobDetail.JobType) as IJob; return (scope.ServiceProvider.GetService(bundle.JobDetail.JobType) as IJob)!;
} }
public void ReturnJob(IJob job) public void ReturnJob(IJob job)

View File

@ -3,6 +3,8 @@ using Quartz;
namespace Bit.Core.Jobs; namespace Bit.Core.Jobs;
#nullable enable
public class JobListener : IJobListener public class JobListener : IJobListener
{ {
private readonly ILogger<JobListener> _logger; private readonly ILogger<JobListener> _logger;
@ -28,7 +30,7 @@ public class JobListener : IJobListener
return Task.FromResult(0); return Task.FromResult(0);
} }
public Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, public Task JobWasExecuted(IJobExecutionContext context, JobExecutionException? jobException,
CancellationToken cancellationToken = default(CancellationToken)) CancellationToken cancellationToken = default(CancellationToken))
{ {
_logger.LogInformation(Constants.BypassFiltersEventId, null, "Finished job {0} at {1}.", _logger.LogInformation(Constants.BypassFiltersEventId, null, "Finished job {0} at {1}.",

View File

@ -2,6 +2,8 @@
namespace Bit.Core.NotificationHub; namespace Bit.Core.NotificationHub;
#nullable enable
public interface INotificationHubProxy public interface INotificationHubProxy
{ {
Task<(INotificationHubClient Client, NotificationOutcome Outcome)[]> SendTemplateNotificationAsync(IDictionary<string, string> properties, string tagExpression); Task<(INotificationHubClient Client, NotificationOutcome Outcome)[]> SendTemplateNotificationAsync(IDictionary<string, string> properties, string tagExpression);

View File

@ -2,6 +2,8 @@
namespace Bit.Core.NotificationHub; namespace Bit.Core.NotificationHub;
#nullable enable
public interface INotificationHubPool public interface INotificationHubPool
{ {
NotificationHubConnection ConnectionFor(Guid comb); NotificationHubConnection ConnectionFor(Guid comb);

View File

@ -2,6 +2,8 @@
namespace Bit.Core.NotificationHub; namespace Bit.Core.NotificationHub;
#nullable enable
public class NotificationHubClientProxy : INotificationHubProxy public class NotificationHubClientProxy : INotificationHubProxy
{ {
private readonly IEnumerable<INotificationHubClient> _clients; private readonly IEnumerable<INotificationHubClient> _clients;

View File

@ -1,4 +1,5 @@
using System.Security.Cryptography; using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Web; using System.Web;
using Bit.Core.Settings; using Bit.Core.Settings;
@ -7,16 +8,18 @@ using Microsoft.Azure.NotificationHubs;
namespace Bit.Core.NotificationHub; namespace Bit.Core.NotificationHub;
#nullable enable
public class NotificationHubConnection public class NotificationHubConnection
{ {
public string HubName { get; init; } public string? HubName { get; init; }
public string ConnectionString { get; init; } public string? ConnectionString { get; init; }
private Lazy<NotificationHubConnectionStringBuilder> _parsedConnectionString; private Lazy<NotificationHubConnectionStringBuilder> _parsedConnectionString;
public Uri Endpoint => _parsedConnectionString.Value.Endpoint; public Uri Endpoint => _parsedConnectionString.Value.Endpoint;
private string SasKey => _parsedConnectionString.Value.SharedAccessKey; private string SasKey => _parsedConnectionString.Value.SharedAccessKey;
private string SasKeyName => _parsedConnectionString.Value.SharedAccessKeyName; private string SasKeyName => _parsedConnectionString.Value.SharedAccessKeyName;
public bool EnableSendTracing { get; init; } public bool EnableSendTracing { get; init; }
private NotificationHubClient _hubClient; private NotificationHubClient? _hubClient;
/// <summary> /// <summary>
/// Gets the NotificationHubClient for this connection. /// Gets the NotificationHubClient for this connection.
/// ///
@ -155,9 +158,10 @@ public class NotificationHubConnection
}; };
} }
[MemberNotNull(nameof(_hubClient))]
private NotificationHubConnection Init() private NotificationHubConnection Init()
{ {
HubClient = NotificationHubClient.CreateClientFromConnectionString(ConnectionString, HubName, EnableSendTracing); _hubClient = NotificationHubClient.CreateClientFromConnectionString(ConnectionString, HubName, EnableSendTracing);
return this; return this;
} }

View File

@ -5,6 +5,8 @@ using Microsoft.Extensions.Logging;
namespace Bit.Core.NotificationHub; namespace Bit.Core.NotificationHub;
#nullable enable
public class NotificationHubPool : INotificationHubPool public class NotificationHubPool : INotificationHubPool
{ {
private List<NotificationHubConnection> _connections { get; } private List<NotificationHubConnection> _connections { get; }

View File

@ -19,6 +19,8 @@ using Notification = Bit.Core.NotificationCenter.Entities.Notification;
namespace Bit.Core.NotificationHub; namespace Bit.Core.NotificationHub;
#nullable enable
/// <summary> /// <summary>
/// Sends mobile push notifications to the Azure Notification Hub. /// Sends mobile push notifications to the Azure Notification Hub.
/// Used by Cloud-Hosted environments. /// Used by Cloud-Hosted environments.

View File

@ -13,6 +13,8 @@ using Microsoft.Extensions.Logging;
namespace Bit.Core.NotificationHub; namespace Bit.Core.NotificationHub;
#nullable enable
public class NotificationHubPushRegistrationService : IPushRegistrationService public class NotificationHubPushRegistrationService : IPushRegistrationService
{ {
private static readonly JsonSerializerOptions webPushSerializationOptions = new() private static readonly JsonSerializerOptions webPushSerializationOptions = new()
@ -37,7 +39,7 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
} }
public async Task CreateOrUpdateRegistrationAsync(PushRegistrationData data, string deviceId, string userId, public async Task CreateOrUpdateRegistrationAsync(PushRegistrationData data, string deviceId, string userId,
string identifier, DeviceType type, IEnumerable<string> organizationIds, Guid installationId) string? identifier, DeviceType type, IEnumerable<string> organizationIds, Guid installationId)
{ {
var orgIds = organizationIds.ToList(); var orgIds = organizationIds.ToList();
var clientType = DeviceTypes.ToClientType(type); var clientType = DeviceTypes.ToClientType(type);
@ -79,7 +81,7 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
} }
private async Task CreateOrUpdateMobileRegistrationAsync(Installation installation, string userId, private async Task CreateOrUpdateMobileRegistrationAsync(Installation installation, string userId,
string identifier, ClientType clientType, List<string> organizationIds, DeviceType type, Guid installationId) string? identifier, ClientType clientType, List<string> organizationIds, DeviceType type, Guid installationId)
{ {
if (string.IsNullOrWhiteSpace(installation.PushChannel)) if (string.IsNullOrWhiteSpace(installation.PushChannel))
{ {
@ -137,7 +139,7 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
} }
private async Task CreateOrUpdateWebRegistrationAsync(string endpoint, string p256dh, string auth, Installation installation, string userId, private async Task CreateOrUpdateWebRegistrationAsync(string endpoint, string p256dh, string auth, Installation installation, string userId,
string identifier, ClientType clientType, List<string> organizationIds, Guid installationId) string? identifier, ClientType clientType, List<string> organizationIds, Guid installationId)
{ {
// The Azure SDK is currently lacking support for web push registrations. // The Azure SDK is currently lacking support for web push registrations.
// We need to use the REST API directly. // We need to use the REST API directly.
@ -187,7 +189,7 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
} }
private static KeyValuePair<string, InstallationTemplate> BuildInstallationTemplate(string templateId, [StringSyntax(StringSyntaxAttribute.Json)] string templateBody, private static KeyValuePair<string, InstallationTemplate> BuildInstallationTemplate(string templateId, [StringSyntax(StringSyntaxAttribute.Json)] string templateBody,
string userId, string identifier, ClientType clientType, List<string> organizationIds, Guid installationId) string userId, string? identifier, ClientType clientType, List<string> organizationIds, Guid installationId)
{ {
var fullTemplateId = $"template:{templateId}"; var fullTemplateId = $"template:{templateId}";

View File

@ -1,5 +1,7 @@
namespace Bit.Core.NotificationHub; namespace Bit.Core.NotificationHub;
#nullable enable
public record struct WebPushRegistrationData public record struct WebPushRegistrationData
{ {
public string Endpoint { get; init; } public string Endpoint { get; init; }
@ -9,9 +11,9 @@ public record struct WebPushRegistrationData
public record class PushRegistrationData public record class PushRegistrationData
{ {
public string Token { get; set; } public string? Token { get; set; }
public WebPushRegistrationData? WebPush { get; set; } public WebPushRegistrationData? WebPush { get; set; }
public PushRegistrationData(string token) public PushRegistrationData(string? token)
{ {
Token = token; Token = token;
} }

View File

@ -842,7 +842,13 @@ public class StripePaymentService : IPaymentService
try try
{ {
await _stripeAdapter.TaxIdCreateAsync(customer.Id, await _stripeAdapter.TaxIdCreateAsync(customer.Id,
new TaxIdCreateOptions { Type = taxInfo.TaxIdType, Value = taxInfo.TaxIdNumber, }); new TaxIdCreateOptions { Type = taxInfo.TaxIdType, Value = taxInfo.TaxIdNumber });
if (taxInfo.TaxIdType == StripeConstants.TaxIdType.SpanishNIF)
{
await _stripeAdapter.TaxIdCreateAsync(customer.Id,
new TaxIdCreateOptions { Type = StripeConstants.TaxIdType.EUVAT, Value = $"ES{taxInfo.TaxIdNumber}" });
}
} }
catch (StripeException e) catch (StripeException e)
{ {
@ -1000,6 +1006,15 @@ public class StripePaymentService : IPaymentService
Value = parameters.TaxInformation.TaxId Value = parameters.TaxInformation.TaxId
} }
]; ];
if (taxIdType == StripeConstants.TaxIdType.SpanishNIF)
{
options.CustomerDetails.TaxIds.Add(new InvoiceCustomerDetailsTaxIdOptions
{
Type = StripeConstants.TaxIdType.EUVAT,
Value = $"ES{parameters.TaxInformation.TaxId}"
});
}
} }
if (!string.IsNullOrWhiteSpace(gatewayCustomerId)) if (!string.IsNullOrWhiteSpace(gatewayCustomerId))
@ -1154,6 +1169,15 @@ public class StripePaymentService : IPaymentService
Value = parameters.TaxInformation.TaxId Value = parameters.TaxInformation.TaxId
} }
]; ];
if (taxIdType == StripeConstants.TaxIdType.SpanishNIF)
{
options.CustomerDetails.TaxIds.Add(new InvoiceCustomerDetailsTaxIdOptions
{
Type = StripeConstants.TaxIdType.EUVAT,
Value = $"ES{parameters.TaxInformation.TaxId}"
});
}
} }
Customer gatewayCustomer = null; Customer gatewayCustomer = null;

View File

@ -821,11 +821,6 @@ public class CipherService : ICipherService
private async Task<bool> UserCanDeleteAsync(CipherDetails cipher, Guid userId) private async Task<bool> UserCanDeleteAsync(CipherDetails cipher, Guid userId)
{ {
if (!_featureService.IsEnabled(FeatureFlagKeys.LimitItemDeletion))
{
return await UserCanEditAsync(cipher, userId);
}
var user = await _userService.GetUserByIdAsync(userId); var user = await _userService.GetUserByIdAsync(userId);
var organizationAbility = cipher.OrganizationId.HasValue ? var organizationAbility = cipher.OrganizationId.HasValue ?
await _applicationCacheService.GetOrganizationAbilityAsync(cipher.OrganizationId.Value) : null; await _applicationCacheService.GetOrganizationAbilityAsync(cipher.OrganizationId.Value) : null;
@ -835,11 +830,6 @@ public class CipherService : ICipherService
private async Task<bool> UserCanRestoreAsync(CipherDetails cipher, Guid userId) private async Task<bool> UserCanRestoreAsync(CipherDetails cipher, Guid userId)
{ {
if (!_featureService.IsEnabled(FeatureFlagKeys.LimitItemDeletion))
{
return await UserCanEditAsync(cipher, userId);
}
var user = await _userService.GetUserByIdAsync(userId); var user = await _userService.GetUserByIdAsync(userId);
var organizationAbility = cipher.OrganizationId.HasValue ? var organizationAbility = cipher.OrganizationId.HasValue ?
await _applicationCacheService.GetOrganizationAbilityAsync(cipher.OrganizationId.Value) : null; await _applicationCacheService.GetOrganizationAbilityAsync(cipher.OrganizationId.Value) : null;
@ -1059,17 +1049,11 @@ public class CipherService : ICipherService
} }
// This method is used to filter ciphers based on the user's permissions to delete them. // This method is used to filter ciphers based on the user's permissions to delete them.
// It supports both the old and new logic depending on the feature flag.
private async Task<List<T>> FilterCiphersByDeletePermission<T>( private async Task<List<T>> FilterCiphersByDeletePermission<T>(
IEnumerable<T> ciphers, IEnumerable<T> ciphers,
HashSet<Guid> cipherIdsSet, HashSet<Guid> cipherIdsSet,
Guid userId) where T : CipherDetails Guid userId) where T : CipherDetails
{ {
if (!_featureService.IsEnabled(FeatureFlagKeys.LimitItemDeletion))
{
return ciphers.Where(c => cipherIdsSet.Contains(c.Id) && c.Edit).ToList();
}
var user = await _userService.GetUserByIdAsync(userId); var user = await _userService.GetUserByIdAsync(userId);
var organizationAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync(); var organizationAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();

View File

@ -8,16 +8,6 @@ namespace Bit.Icons.Controllers;
[Route("")] [Route("")]
public class IconsController : Controller public class IconsController : Controller
{ {
// Basic bwi-globe icon
private static readonly byte[] _notFoundImage = Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUg" +
"AAABMAAAATCAQAAADYWf5HAAABu0lEQVR42nXSvWuTURTH8R+t0heI9Y04aJycdBLNJNrBFBU7OFgUER3q21I0bXK+JwZ" +
"pXISm/QdcRB3EgqBBsNihsUbbgODQQSKCuKSDOApJuuhj8tCYQj/jvYfD795z1MZ+nBKrNKhSwrMxbZTrtRnqlEjZkB/x" +
"C/xmhZrlc71qS0Up8yVzTCGucFNKD1JhORVd70SZNU4okNx5d4+U2UXRIpJFWLClsR79YzN88wQvLWNzzPKEeS/wkQGpW" +
"VhhqhW8TtDJD3Mm1x/23zLSrZCdpBY8BueTNjHSbc+8wC9HlHgU5Aj5AW5zPdcVdpq0UcknWBSr/pjixO4gfp899Kd23p" +
"M2qQCH7LkCnqAqGh73OK/8NPOcaibr90LrW/yWAnaUhqjaOSl9nFR2r5rsqo22ypn1B5IN8VOUMHVgOnNQIX+d62plcz6" +
"rg1/jskK8CMb4we4pG6OWHtR/LBJkC2E4a7ZPkuX5ntumAOM2xxveclEhLvGH6XCmLPs735Eetrw63NnOgr9P9q1viC3x" +
"lRUGOjImqFDuOBvrYYoaZU9z1uPpYae5NfdvbNVG2ZjDIlXq/oMi46lo++4vjjPBl2Dlg00AAAAASUVORK5CYII=");
private readonly IMemoryCache _memoryCache; private readonly IMemoryCache _memoryCache;
private readonly IDomainMappingService _domainMappingService; private readonly IDomainMappingService _domainMappingService;
private readonly IIconFetchingService _iconFetchingService; private readonly IIconFetchingService _iconFetchingService;
@ -99,7 +89,7 @@ public class IconsController : Controller
if (icon == null) if (icon == null)
{ {
return new FileContentResult(_notFoundImage, "image/png"); return new NotFoundResult();
} }
return new FileContentResult(icon.Image, icon.Format); return new FileContentResult(icon.Image, icon.Format);

View File

@ -2,6 +2,7 @@
using Bit.Core.Auth.Repositories; using Bit.Core.Auth.Repositories;
using Bit.Core.Billing.Providers.Repositories; using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Repositories;
using Bit.Core.Dirt.Reports.Repositories;
using Bit.Core.KeyManagement.Repositories; using Bit.Core.KeyManagement.Repositories;
using Bit.Core.NotificationCenter.Repositories; using Bit.Core.NotificationCenter.Repositories;
using Bit.Core.Platform.Installations; using Bit.Core.Platform.Installations;
@ -12,6 +13,7 @@ using Bit.Core.Vault.Repositories;
using Bit.Infrastructure.Dapper.AdminConsole.Repositories; using Bit.Infrastructure.Dapper.AdminConsole.Repositories;
using Bit.Infrastructure.Dapper.Auth.Repositories; using Bit.Infrastructure.Dapper.Auth.Repositories;
using Bit.Infrastructure.Dapper.Billing.Repositories; using Bit.Infrastructure.Dapper.Billing.Repositories;
using Bit.Infrastructure.Dapper.Dirt;
using Bit.Infrastructure.Dapper.KeyManagement.Repositories; using Bit.Infrastructure.Dapper.KeyManagement.Repositories;
using Bit.Infrastructure.Dapper.NotificationCenter.Repositories; using Bit.Infrastructure.Dapper.NotificationCenter.Repositories;
using Bit.Infrastructure.Dapper.Platform; using Bit.Infrastructure.Dapper.Platform;

View File

@ -1,14 +1,14 @@
using System.Data; using System.Data;
using Bit.Core.Dirt.Reports.Entities;
using Bit.Core.Dirt.Reports.Repositories;
using Bit.Core.Settings; using Bit.Core.Settings;
using Bit.Core.Tools.Repositories;
using Bit.Infrastructure.Dapper.Repositories; using Bit.Infrastructure.Dapper.Repositories;
using Dapper; using Dapper;
using Microsoft.Data.SqlClient; using Microsoft.Data.SqlClient;
using ToolsEntities = Bit.Core.Tools.Entities;
namespace Bit.Infrastructure.Dapper.Tools.Repositories; namespace Bit.Infrastructure.Dapper.Dirt;
public class PasswordHealthReportApplicationRepository : Repository<ToolsEntities.PasswordHealthReportApplication, Guid>, IPasswordHealthReportApplicationRepository public class PasswordHealthReportApplicationRepository : Repository<PasswordHealthReportApplication, Guid>, IPasswordHealthReportApplicationRepository
{ {
public PasswordHealthReportApplicationRepository(GlobalSettings globalSettings) public PasswordHealthReportApplicationRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString) : this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
@ -18,11 +18,11 @@ public class PasswordHealthReportApplicationRepository : Repository<ToolsEntitie
: base(connectionString, readOnlyConnectionString) : base(connectionString, readOnlyConnectionString)
{ } { }
public async Task<ICollection<ToolsEntities.PasswordHealthReportApplication>> GetByOrganizationIdAsync(Guid organizationId) public async Task<ICollection<PasswordHealthReportApplication>> GetByOrganizationIdAsync(Guid organizationId)
{ {
using (var connection = new SqlConnection(ReadOnlyConnectionString)) using (var connection = new SqlConnection(ReadOnlyConnectionString))
{ {
var results = await connection.QueryAsync<ToolsEntities.PasswordHealthReportApplication>( var results = await connection.QueryAsync<PasswordHealthReportApplication>(
$"[{Schema}].[PasswordHealthReportApplication_ReadByOrganizationId]", $"[{Schema}].[PasswordHealthReportApplication_ReadByOrganizationId]",
new { OrganizationId = organizationId }, new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);

View File

@ -1,8 +1,8 @@
using Bit.Infrastructure.EntityFramework.Tools.Models; using Bit.Infrastructure.EntityFramework.Dirt.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Bit.Infrastructure.EntityFramework.Tools.Configurations; namespace Bit.Infrastructure.EntityFramework.Dirt.Configurations;
public class PasswordHealthReportApplicationEntityTypeConfiguration : IEntityTypeConfiguration<PasswordHealthReportApplication> public class PasswordHealthReportApplicationEntityTypeConfiguration : IEntityTypeConfiguration<PasswordHealthReportApplication>
{ {

View File

@ -1,9 +1,9 @@
using AutoMapper; using AutoMapper;
using Bit.Infrastructure.EntityFramework.AdminConsole.Models; using Bit.Infrastructure.EntityFramework.AdminConsole.Models;
namespace Bit.Infrastructure.EntityFramework.Tools.Models; namespace Bit.Infrastructure.EntityFramework.Dirt.Models;
public class PasswordHealthReportApplication : Core.Tools.Entities.PasswordHealthReportApplication public class PasswordHealthReportApplication : Core.Dirt.Reports.Entities.PasswordHealthReportApplication
{ {
public virtual Organization Organization { get; set; } public virtual Organization Organization { get; set; }
} }
@ -12,7 +12,7 @@ public class PasswordHealthReportApplicationProfile : Profile
{ {
public PasswordHealthReportApplicationProfile() public PasswordHealthReportApplicationProfile()
{ {
CreateMap<Core.Tools.Entities.PasswordHealthReportApplication, PasswordHealthReportApplication>() CreateMap<Core.Dirt.Reports.Entities.PasswordHealthReportApplication, PasswordHealthReportApplication>()
.ReverseMap(); .ReverseMap();
} }
} }

View File

@ -1,22 +1,21 @@
using AutoMapper; using AutoMapper;
using Bit.Core.Tools.Repositories; using Bit.Core.Dirt.Reports.Repositories;
using Bit.Infrastructure.EntityFramework.Dirt.Models;
using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Infrastructure.EntityFramework.Repositories;
using Bit.Infrastructure.EntityFramework.Tools.Models;
using LinqToDB; using LinqToDB;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using AdminConsoleEntities = Bit.Core.Tools.Entities;
namespace Bit.Infrastructure.EntityFramework.Tools.Repositories; namespace Bit.Infrastructure.EntityFramework.Dirt.Repositories;
public class PasswordHealthReportApplicationRepository : public class PasswordHealthReportApplicationRepository :
Repository<AdminConsoleEntities.PasswordHealthReportApplication, PasswordHealthReportApplication, Guid>, Repository<Core.Dirt.Reports.Entities.PasswordHealthReportApplication, PasswordHealthReportApplication, Guid>,
IPasswordHealthReportApplicationRepository IPasswordHealthReportApplicationRepository
{ {
public PasswordHealthReportApplicationRepository(IServiceScopeFactory serviceScopeFactory, public PasswordHealthReportApplicationRepository(IServiceScopeFactory serviceScopeFactory,
IMapper mapper) : base(serviceScopeFactory, mapper, (DatabaseContext context) => context.PasswordHealthReportApplications) IMapper mapper) : base(serviceScopeFactory, mapper, (DatabaseContext context) => context.PasswordHealthReportApplications)
{ } { }
public async Task<ICollection<AdminConsoleEntities.PasswordHealthReportApplication>> GetByOrganizationIdAsync(Guid organizationId) public async Task<ICollection<Core.Dirt.Reports.Entities.PasswordHealthReportApplication>> GetByOrganizationIdAsync(Guid organizationId)
{ {
using (var scope = ServiceScopeFactory.CreateScope()) using (var scope = ServiceScopeFactory.CreateScope())
{ {
@ -24,7 +23,7 @@ public class PasswordHealthReportApplicationRepository :
var results = await dbContext.PasswordHealthReportApplications var results = await dbContext.PasswordHealthReportApplications
.Where(p => p.OrganizationId == organizationId) .Where(p => p.OrganizationId == organizationId)
.ToListAsync(); .ToListAsync();
return Mapper.Map<ICollection<AdminConsoleEntities.PasswordHealthReportApplication>>(results); return Mapper.Map<ICollection<Core.Dirt.Reports.Entities.PasswordHealthReportApplication>>(results);
} }
} }
} }

View File

@ -2,6 +2,7 @@
using Bit.Core.Auth.Repositories; using Bit.Core.Auth.Repositories;
using Bit.Core.Billing.Providers.Repositories; using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Repositories;
using Bit.Core.Dirt.Reports.Repositories;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.KeyManagement.Repositories; using Bit.Core.KeyManagement.Repositories;
using Bit.Core.NotificationCenter.Repositories; using Bit.Core.NotificationCenter.Repositories;
@ -13,6 +14,7 @@ using Bit.Core.Vault.Repositories;
using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories; using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories;
using Bit.Infrastructure.EntityFramework.Auth.Repositories; using Bit.Infrastructure.EntityFramework.Auth.Repositories;
using Bit.Infrastructure.EntityFramework.Billing.Repositories; using Bit.Infrastructure.EntityFramework.Billing.Repositories;
using Bit.Infrastructure.EntityFramework.Dirt.Repositories;
using Bit.Infrastructure.EntityFramework.KeyManagement.Repositories; using Bit.Infrastructure.EntityFramework.KeyManagement.Repositories;
using Bit.Infrastructure.EntityFramework.NotificationCenter.Repositories; using Bit.Infrastructure.EntityFramework.NotificationCenter.Repositories;
using Bit.Infrastructure.EntityFramework.Platform; using Bit.Infrastructure.EntityFramework.Platform;

View File

@ -4,11 +4,11 @@ using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider;
using Bit.Infrastructure.EntityFramework.Auth.Models; using Bit.Infrastructure.EntityFramework.Auth.Models;
using Bit.Infrastructure.EntityFramework.Billing.Models; using Bit.Infrastructure.EntityFramework.Billing.Models;
using Bit.Infrastructure.EntityFramework.Converters; using Bit.Infrastructure.EntityFramework.Converters;
using Bit.Infrastructure.EntityFramework.Dirt.Models;
using Bit.Infrastructure.EntityFramework.Models; using Bit.Infrastructure.EntityFramework.Models;
using Bit.Infrastructure.EntityFramework.NotificationCenter.Models; using Bit.Infrastructure.EntityFramework.NotificationCenter.Models;
using Bit.Infrastructure.EntityFramework.Platform; using Bit.Infrastructure.EntityFramework.Platform;
using Bit.Infrastructure.EntityFramework.SecretsManager.Models; using Bit.Infrastructure.EntityFramework.SecretsManager.Models;
using Bit.Infrastructure.EntityFramework.Tools.Models;
using Bit.Infrastructure.EntityFramework.Vault.Models; using Bit.Infrastructure.EntityFramework.Vault.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@ -7,7 +7,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="7.2.0" /> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="7.3.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -23,6 +23,7 @@ using Bit.Core.Auth.UserFeatures;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Services;
using Bit.Core.Billing.Services.Implementations; using Bit.Core.Billing.Services.Implementations;
using Bit.Core.Billing.TrialInitiation; using Bit.Core.Billing.TrialInitiation;
using Bit.Core.Dirt.Reports.ReportFeatures;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.HostedServices; using Bit.Core.HostedServices;
@ -43,7 +44,6 @@ using Bit.Core.Services;
using Bit.Core.Settings; using Bit.Core.Settings;
using Bit.Core.Tokens; using Bit.Core.Tokens;
using Bit.Core.Tools.ImportFeatures; using Bit.Core.Tools.ImportFeatures;
using Bit.Core.Tools.ReportFeatures;
using Bit.Core.Tools.SendFeatures; using Bit.Core.Tools.SendFeatures;
using Bit.Core.Tools.Services; using Bit.Core.Tools.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;

Some files were not shown because too many files have changed in this diff Show More