mirror of
https://github.com/bitwarden/server.git
synced 2025-06-07 19:50:32 -05:00
Merge branch 'main' into billing/license-refactor
This commit is contained in:
commit
71cb4a4ea8
@ -31,6 +31,7 @@ public record PlanAdapter : Plan
|
||||
HasScim = HasFeature("scim");
|
||||
HasResetPassword = HasFeature("resetPassword");
|
||||
UsersGetPremium = HasFeature("usersGetPremium");
|
||||
HasCustomPermissions = HasFeature("customPermissions");
|
||||
UpgradeSortOrder = plan.AdditionalData.TryGetValue("upgradeSortOrder", out var upgradeSortOrder)
|
||||
? int.Parse(upgradeSortOrder)
|
||||
: 0;
|
||||
@ -141,6 +142,7 @@ public record PlanAdapter : Plan
|
||||
var stripeSeatPlanId = GetStripeSeatPlanId(seats);
|
||||
var hasAdditionalSeatsOption = seats.IsScalable;
|
||||
var seatPrice = GetSeatPrice(seats);
|
||||
var baseSeats = GetBaseSeats(seats);
|
||||
var maxSeats = GetMaxSeats(seats);
|
||||
var allowSeatAutoscale = seats.IsScalable;
|
||||
var maxProjects = plan.AdditionalData.TryGetValue("secretsManager.maxProjects", out var value) ? short.Parse(value) : 0;
|
||||
@ -156,6 +158,7 @@ public record PlanAdapter : Plan
|
||||
StripeSeatPlanId = stripeSeatPlanId,
|
||||
HasAdditionalSeatsOption = hasAdditionalSeatsOption,
|
||||
SeatPrice = seatPrice,
|
||||
BaseSeats = baseSeats,
|
||||
MaxSeats = maxSeats,
|
||||
AllowSeatAutoscale = allowSeatAutoscale,
|
||||
MaxProjects = maxProjects
|
||||
@ -168,8 +171,16 @@ public record PlanAdapter : Plan
|
||||
private static decimal GetBasePrice(PurchasableDTO purchasable)
|
||||
=> 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)
|
||||
=> purchasable.FromPackaged(x => x.Quantity);
|
||||
=> purchasable.Match(
|
||||
free => free.Quantity,
|
||||
packaged => packaged.Quantity,
|
||||
scalable => scalable.Provided);
|
||||
|
||||
private static short GetBaseServiceAccount(FreeOrScalableDTO freeOrScalable)
|
||||
=> freeOrScalable.Match(
|
||||
|
@ -3,6 +3,8 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace Bit.Core.Exceptions;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class BadRequestException : Exception
|
||||
{
|
||||
public BadRequestException() : base()
|
||||
@ -41,5 +43,5 @@ public class BadRequestException : Exception
|
||||
}
|
||||
}
|
||||
|
||||
public ModelStateDictionary ModelState { get; set; }
|
||||
public ModelStateDictionary? ModelState { get; set; }
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
namespace Bit.Core.Exceptions;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class ConflictException : Exception
|
||||
{
|
||||
public ConflictException() : base("Conflict.") { }
|
||||
|
@ -1,5 +1,7 @@
|
||||
namespace Bit.Core.Exceptions;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class DnsQueryException : Exception
|
||||
{
|
||||
public DnsQueryException(string message)
|
||||
|
@ -1,5 +1,7 @@
|
||||
namespace Bit.Core.Exceptions;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class DomainClaimedException : Exception
|
||||
{
|
||||
public DomainClaimedException()
|
||||
|
@ -1,5 +1,7 @@
|
||||
namespace Bit.Core.Exceptions;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class DomainVerifiedException : Exception
|
||||
{
|
||||
public DomainVerifiedException()
|
||||
|
@ -1,5 +1,7 @@
|
||||
namespace Bit.Core.Exceptions;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class DuplicateDomainException : Exception
|
||||
{
|
||||
public DuplicateDomainException()
|
||||
|
@ -1,5 +1,7 @@
|
||||
namespace Bit.Core.Exceptions;
|
||||
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Exception to throw when a requested feature is not yet enabled/available for the requesting context.
|
||||
/// </summary>
|
||||
|
@ -1,8 +1,10 @@
|
||||
namespace Bit.Core.Exceptions;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class GatewayException : Exception
|
||||
{
|
||||
public GatewayException(string message, Exception innerException = null)
|
||||
public GatewayException(string message, Exception? innerException = null)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
namespace Bit.Core.Exceptions;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class InvalidEmailException : Exception
|
||||
{
|
||||
public InvalidEmailException()
|
||||
|
@ -1,5 +1,7 @@
|
||||
namespace Bit.Core.Exceptions;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class InvalidGatewayCustomerIdException : Exception
|
||||
{
|
||||
public InvalidGatewayCustomerIdException()
|
||||
|
@ -1,5 +1,7 @@
|
||||
namespace Bit.Core.Exceptions;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class NotFoundException : Exception
|
||||
{
|
||||
public NotFoundException() : base()
|
||||
|
@ -10,9 +10,11 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.HostedServices;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class ApplicationCacheHostedService : IHostedService, IDisposable
|
||||
{
|
||||
private readonly InMemoryServiceBusApplicationCacheService _applicationCacheService;
|
||||
private readonly InMemoryServiceBusApplicationCacheService? _applicationCacheService;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
protected readonly ILogger<ApplicationCacheHostedService> _logger;
|
||||
private readonly ServiceBusClient _serviceBusClient;
|
||||
@ -20,8 +22,8 @@ public class ApplicationCacheHostedService : IHostedService, IDisposable
|
||||
private readonly ServiceBusAdministrationClient _serviceBusAdministrationClient;
|
||||
private readonly string _subName;
|
||||
private readonly string _topicName;
|
||||
private CancellationTokenSource _cts;
|
||||
private Task _executingTask;
|
||||
private CancellationTokenSource? _cts;
|
||||
private Task? _executingTask;
|
||||
|
||||
|
||||
public ApplicationCacheHostedService(
|
||||
@ -67,14 +69,18 @@ public class ApplicationCacheHostedService : IHostedService, IDisposable
|
||||
{
|
||||
await _subscriptionReceiver.CloseAsync(cancellationToken);
|
||||
await _serviceBusClient.DisposeAsync();
|
||||
_cts.Cancel();
|
||||
_cts?.Cancel();
|
||||
try
|
||||
{
|
||||
await _serviceBusAdministrationClient.DeleteSubscriptionAsync(_topicName, _subName, cancellationToken);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (_executingTask != null)
|
||||
{
|
||||
await _executingTask;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{ }
|
||||
|
@ -3,6 +3,8 @@ using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Bit.Core.HostedServices;
|
||||
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// A startup service that will seed the IP rate limiting stores with any values in the
|
||||
/// GlobalSettings configuration.
|
||||
|
@ -3,6 +3,8 @@ using Quartz;
|
||||
|
||||
namespace Bit.Core.Jobs;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public abstract class BaseJob : IJob
|
||||
{
|
||||
protected readonly ILogger _logger;
|
||||
|
@ -8,6 +8,8 @@ using Quartz.Impl.Matchers;
|
||||
|
||||
namespace Bit.Core.Jobs;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public abstract class BaseJobsHostedService : IHostedService, IDisposable
|
||||
{
|
||||
private const int MaximumJobRetries = 10;
|
||||
@ -16,7 +18,7 @@ public abstract class BaseJobsHostedService : IHostedService, IDisposable
|
||||
private readonly ILogger<JobListener> _listenerLogger;
|
||||
protected readonly ILogger _logger;
|
||||
|
||||
private IScheduler _scheduler;
|
||||
private IScheduler? _scheduler;
|
||||
protected GlobalSettings _globalSettings;
|
||||
|
||||
public BaseJobsHostedService(
|
||||
@ -31,7 +33,7 @@ public abstract class BaseJobsHostedService : IHostedService, IDisposable
|
||||
_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)
|
||||
{
|
||||
@ -61,10 +63,19 @@ public abstract class BaseJobsHostedService : IHostedService, IDisposable
|
||||
_scheduler.ListenerManager.AddJobListener(new JobListener(_listenerLogger),
|
||||
GroupMatcher<JobKey>.AnyGroup());
|
||||
await _scheduler.Start(cancellationToken);
|
||||
|
||||
var jobKeys = new List<JobKey>();
|
||||
var triggerKeys = new List<TriggerKey>();
|
||||
|
||||
if (Jobs != null)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
// 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)
|
||||
.WithIdentity(job.FullName)
|
||||
.WithIdentity(job.FullName!)
|
||||
.Build();
|
||||
|
||||
var dupeJ = await _scheduler.GetJobDetail(jobDetail.Key);
|
||||
@ -106,13 +117,6 @@ public abstract class BaseJobsHostedService : IHostedService, IDisposable
|
||||
|
||||
// Delete old Jobs and Triggers
|
||||
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)
|
||||
{
|
||||
@ -126,7 +130,6 @@ public abstract class BaseJobsHostedService : IHostedService, IDisposable
|
||||
}
|
||||
|
||||
var existingTriggerKeys = await _scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.AnyGroup());
|
||||
var triggerKeys = Jobs.Select(j => j.Item2.Key);
|
||||
|
||||
foreach (var key in existingTriggerKeys)
|
||||
{
|
||||
@ -142,7 +145,10 @@ public abstract class BaseJobsHostedService : IHostedService, IDisposable
|
||||
|
||||
public virtual async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await _scheduler?.Shutdown(cancellationToken);
|
||||
if (_scheduler is not null)
|
||||
{
|
||||
await _scheduler.Shutdown(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
|
@ -4,6 +4,8 @@ using Quartz.Spi;
|
||||
|
||||
namespace Bit.Core.Jobs;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class JobFactory : IJobFactory
|
||||
{
|
||||
private readonly IServiceProvider _container;
|
||||
@ -16,7 +18,7 @@ public class JobFactory : IJobFactory
|
||||
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
|
||||
{
|
||||
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)
|
||||
|
@ -3,6 +3,8 @@ using Quartz;
|
||||
|
||||
namespace Bit.Core.Jobs;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class JobListener : IJobListener
|
||||
{
|
||||
private readonly ILogger<JobListener> _logger;
|
||||
@ -28,7 +30,7 @@ public class JobListener : IJobListener
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException,
|
||||
public Task JobWasExecuted(IJobExecutionContext context, JobExecutionException? jobException,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, null, "Finished job {0} at {1}.",
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Bit.Core.NotificationHub;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public interface INotificationHubProxy
|
||||
{
|
||||
Task<(INotificationHubClient Client, NotificationOutcome Outcome)[]> SendTemplateNotificationAsync(IDictionary<string, string> properties, string tagExpression);
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Bit.Core.NotificationHub;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public interface INotificationHubPool
|
||||
{
|
||||
NotificationHubConnection ConnectionFor(Guid comb);
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Bit.Core.NotificationHub;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class NotificationHubClientProxy : INotificationHubProxy
|
||||
{
|
||||
private readonly IEnumerable<INotificationHubClient> _clients;
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using Bit.Core.Settings;
|
||||
@ -7,16 +8,18 @@ using Microsoft.Azure.NotificationHubs;
|
||||
|
||||
namespace Bit.Core.NotificationHub;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class NotificationHubConnection
|
||||
{
|
||||
public string HubName { get; init; }
|
||||
public string ConnectionString { get; init; }
|
||||
public string? HubName { get; init; }
|
||||
public string? ConnectionString { get; init; }
|
||||
private Lazy<NotificationHubConnectionStringBuilder> _parsedConnectionString;
|
||||
public Uri Endpoint => _parsedConnectionString.Value.Endpoint;
|
||||
private string SasKey => _parsedConnectionString.Value.SharedAccessKey;
|
||||
private string SasKeyName => _parsedConnectionString.Value.SharedAccessKeyName;
|
||||
public bool EnableSendTracing { get; init; }
|
||||
private NotificationHubClient _hubClient;
|
||||
private NotificationHubClient? _hubClient;
|
||||
/// <summary>
|
||||
/// Gets the NotificationHubClient for this connection.
|
||||
///
|
||||
@ -155,9 +158,10 @@ public class NotificationHubConnection
|
||||
};
|
||||
}
|
||||
|
||||
[MemberNotNull(nameof(_hubClient))]
|
||||
private NotificationHubConnection Init()
|
||||
{
|
||||
HubClient = NotificationHubClient.CreateClientFromConnectionString(ConnectionString, HubName, EnableSendTracing);
|
||||
_hubClient = NotificationHubClient.CreateClientFromConnectionString(ConnectionString, HubName, EnableSendTracing);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,8 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.NotificationHub;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class NotificationHubPool : INotificationHubPool
|
||||
{
|
||||
private List<NotificationHubConnection> _connections { get; }
|
||||
|
@ -19,6 +19,8 @@ using Notification = Bit.Core.NotificationCenter.Entities.Notification;
|
||||
|
||||
namespace Bit.Core.NotificationHub;
|
||||
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Sends mobile push notifications to the Azure Notification Hub.
|
||||
/// Used by Cloud-Hosted environments.
|
||||
|
@ -13,6 +13,8 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.NotificationHub;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class NotificationHubPushRegistrationService : IPushRegistrationService
|
||||
{
|
||||
private static readonly JsonSerializerOptions webPushSerializationOptions = new()
|
||||
@ -37,7 +39,7 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
|
||||
}
|
||||
|
||||
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 clientType = DeviceTypes.ToClientType(type);
|
||||
@ -79,7 +81,7 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
@ -137,7 +139,7 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
|
||||
}
|
||||
|
||||
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.
|
||||
// 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,
|
||||
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}";
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
namespace Bit.Core.NotificationHub;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public record struct WebPushRegistrationData
|
||||
{
|
||||
public string Endpoint { get; init; }
|
||||
@ -9,9 +11,9 @@ public record struct WebPushRegistrationData
|
||||
|
||||
public record class PushRegistrationData
|
||||
{
|
||||
public string Token { get; set; }
|
||||
public string? Token { get; set; }
|
||||
public WebPushRegistrationData? WebPush { get; set; }
|
||||
public PushRegistrationData(string token)
|
||||
public PushRegistrationData(string? token)
|
||||
{
|
||||
Token = token;
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ public class AccountsKeyManagementControllerTests
|
||||
}
|
||||
catch (BadRequestException ex)
|
||||
{
|
||||
Assert.NotEmpty(ex.ModelState.Values);
|
||||
Assert.NotEmpty(ex.ModelState!.Values);
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,7 +210,7 @@ public class AccountsKeyManagementControllerTests
|
||||
var badRequestException =
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.PostSetKeyConnectorKeyAsync(data));
|
||||
|
||||
Assert.Equal(1, badRequestException.ModelState.ErrorCount);
|
||||
Assert.Equal(1, badRequestException.ModelState!.ErrorCount);
|
||||
Assert.Equal("set key connector key error", badRequestException.ModelState.Root.Errors[0].ErrorMessage);
|
||||
await sutProvider.GetDependency<IUserService>().Received(1)
|
||||
.SetKeyConnectorKeyAsync(Arg.Do<User>(user =>
|
||||
@ -284,7 +284,7 @@ public class AccountsKeyManagementControllerTests
|
||||
var badRequestException =
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.PostConvertToKeyConnectorAsync());
|
||||
|
||||
Assert.Equal(1, badRequestException.ModelState.ErrorCount);
|
||||
Assert.Equal(1, badRequestException.ModelState!.ErrorCount);
|
||||
Assert.Equal("convert to key connector error", badRequestException.ModelState.Root.Errors[0].ErrorMessage);
|
||||
await sutProvider.GetDependency<IUserService>().Received(1)
|
||||
.ConvertToKeyConnectorAsync(Arg.Is(expectedUser));
|
||||
|
Loading…
x
Reference in New Issue
Block a user