1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-25 06:42:22 -05:00

[PM-336] Nullable Platform & Unowned Services (#5646)

* Nullable Platform & Unowned Services

* Fix build errors

* Format
This commit is contained in:
Justin Baur 2025-04-15 12:56:58 -04:00 committed by GitHub
parent 84a984a9e6
commit 2242a70e50
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 141 additions and 84 deletions

View File

@ -1,4 +1,6 @@
namespace Bit.Core.Platform.Installations; #nullable enable
namespace Bit.Core.Platform.Installations;
/// <summary> /// <summary>
/// Command interface responsible for updating data on an `Installation` /// Command interface responsible for updating data on an `Installation`

View File

@ -1,4 +1,6 @@
namespace Bit.Core.Platform.Installations; #nullable enable
namespace Bit.Core.Platform.Installations;
/// <summary> /// <summary>
/// Commands responsible for updating an installation from /// Commands responsible for updating an installation from

View File

@ -1,4 +1,6 @@
namespace Bit.Core.Platform.Installations; #nullable enable
namespace Bit.Core.Platform.Installations;
/// <summary> /// <summary>
/// Queries responsible for fetching an installation from /// Queries responsible for fetching an installation from
@ -19,7 +21,7 @@ public class GetInstallationQuery : IGetInstallationQuery
} }
/// <inheritdoc cref="IGetInstallationQuery.GetByIdAsync"/> /// <inheritdoc cref="IGetInstallationQuery.GetByIdAsync"/>
public async Task<Installation> GetByIdAsync(Guid installationId) public async Task<Installation?> GetByIdAsync(Guid installationId)
{ {
if (installationId == default(Guid)) if (installationId == default(Guid))
{ {

View File

@ -1,4 +1,6 @@
namespace Bit.Core.Platform.Installations; #nullable enable
namespace Bit.Core.Platform.Installations;
/// <summary> /// <summary>
/// Query interface responsible for fetching an installation from /// Query interface responsible for fetching an installation from
@ -16,5 +18,5 @@ public interface IGetInstallationQuery
/// <param name="installationId">The GUID id of the installation.</param> /// <param name="installationId">The GUID id of the installation.</param>
/// <returns>A task containing an `Installation`.</returns> /// <returns>A task containing an `Installation`.</returns>
/// <seealso cref="T:Bit.Core.Platform.Installations.Repositories.IInstallationRepository"/> /// <seealso cref="T:Bit.Core.Platform.Installations.Repositories.IInstallationRepository"/>
Task<Installation> GetByIdAsync(Guid installationId); Task<Installation?> GetByIdAsync(Guid installationId);
} }

View File

@ -1,4 +1,6 @@
using Bit.Core.Platform.Installations; #nullable enable
using Bit.Core.Platform.Installations;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Bit.Core.Platform; namespace Bit.Core.Platform;

View File

@ -1,4 +1,6 @@
using Bit.Core.Enums; #nullable enable
using Bit.Core.Enums;
using Bit.Core.NotificationHub; using Bit.Core.NotificationHub;
namespace Bit.Core.Platform.Push; namespace Bit.Core.Platform.Push;

View File

@ -1,4 +1,6 @@
using Bit.Core.Enums; #nullable enable
using Bit.Core.Enums;
using Bit.Core.NotificationHub; using Bit.Core.NotificationHub;
namespace Bit.Core.Platform.Push.Internal; namespace Bit.Core.Platform.Push.Internal;

View File

@ -1,4 +1,6 @@
using Bit.Core.Enums; #nullable enable
using Bit.Core.Enums;
using Bit.Core.IdentityServer; using Bit.Core.IdentityServer;
using Bit.Core.Models.Api; using Bit.Core.Models.Api;
using Bit.Core.NotificationHub; using Bit.Core.NotificationHub;

View File

@ -48,7 +48,7 @@ public interface ICollectionRepository : IRepository<Collection, Guid>
Task<CollectionAdminDetails?> GetByIdWithPermissionsAsync(Guid collectionId, Guid? userId, bool includeAccessRelationships); Task<CollectionAdminDetails?> GetByIdWithPermissionsAsync(Guid collectionId, Guid? userId, bool includeAccessRelationships);
Task CreateAsync(Collection obj, IEnumerable<CollectionAccessSelection>? groups, IEnumerable<CollectionAccessSelection>? users); Task CreateAsync(Collection obj, IEnumerable<CollectionAccessSelection>? groups, IEnumerable<CollectionAccessSelection>? users);
Task ReplaceAsync(Collection obj, IEnumerable<CollectionAccessSelection> groups, IEnumerable<CollectionAccessSelection> users); Task ReplaceAsync(Collection obj, IEnumerable<CollectionAccessSelection>? groups, IEnumerable<CollectionAccessSelection>? users);
Task DeleteUserAsync(Guid collectionId, Guid organizationUserId); Task DeleteUserAsync(Guid collectionId, Guid organizationUserId);
Task UpdateUsersAsync(Guid id, IEnumerable<CollectionAccessSelection> users); Task UpdateUsersAsync(Guid id, IEnumerable<CollectionAccessSelection> users);
Task<ICollection<CollectionAccessSelection>> GetManyUsersByIdAsync(Guid id); Task<ICollection<CollectionAccessSelection>> GetManyUsersByIdAsync(Guid id);

View File

@ -1,4 +1,6 @@
using System.Security.Claims; #nullable enable
using System.Security.Claims;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Models.Business; using Bit.Core.Models.Business;
@ -12,14 +14,14 @@ public interface ILicensingService
Task<bool> ValidateUserPremiumAsync(User user); Task<bool> ValidateUserPremiumAsync(User user);
bool VerifyLicense(ILicense license); bool VerifyLicense(ILicense license);
byte[] SignLicense(ILicense license); byte[] SignLicense(ILicense license);
Task<OrganizationLicense> ReadOrganizationLicenseAsync(Organization organization); Task<OrganizationLicense?> ReadOrganizationLicenseAsync(Organization organization);
Task<OrganizationLicense> ReadOrganizationLicenseAsync(Guid organizationId); Task<OrganizationLicense?> ReadOrganizationLicenseAsync(Guid organizationId);
ClaimsPrincipal GetClaimsPrincipalFromLicense(ILicense license); ClaimsPrincipal? GetClaimsPrincipalFromLicense(ILicense license);
Task<string> CreateOrganizationTokenAsync( Task<string?> CreateOrganizationTokenAsync(
Organization organization, Organization organization,
Guid installationId, Guid installationId,
SubscriptionInfo subscriptionInfo); SubscriptionInfo subscriptionInfo);
Task<string> CreateUserTokenAsync(User user, SubscriptionInfo subscriptionInfo); Task<string?> CreateUserTokenAsync(User user, SubscriptionInfo subscriptionInfo);
} }

View File

@ -1,4 +1,6 @@
using Bit.Core.AdminConsole.Entities; #nullable enable
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.Auth.Entities; using Bit.Core.Auth.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
@ -55,7 +57,7 @@ public interface IMailService
bool mentionInvoices); bool mentionInvoices);
Task SendPaymentFailedAsync(string email, decimal amount, bool mentionInvoices); Task SendPaymentFailedAsync(string email, decimal amount, bool mentionInvoices);
Task SendAddedCreditAsync(string email, decimal amount); Task SendAddedCreditAsync(string email, decimal amount);
Task SendLicenseExpiredAsync(IEnumerable<string> emails, string organizationName = null); Task SendLicenseExpiredAsync(IEnumerable<string> emails, string? organizationName = null);
Task SendNewDeviceLoggedInEmail(string email, string deviceType, DateTime timestamp, string ip); Task SendNewDeviceLoggedInEmail(string email, string deviceType, DateTime timestamp, string ip);
Task SendRecoverTwoFactorEmail(string email, DateTime timestamp, string ip); Task SendRecoverTwoFactorEmail(string email, DateTime timestamp, string ip);
Task SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(string organizationName, string email); Task SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(string organizationName, string email);
@ -96,9 +98,11 @@ public interface IMailService
Task SendInitiateDeletProviderEmailAsync(string email, Provider provider, string token); Task SendInitiateDeletProviderEmailAsync(string email, Provider provider, string token);
Task SendInitiateDeleteOrganzationEmailAsync(string email, Organization organization, string token); Task SendInitiateDeleteOrganzationEmailAsync(string email, Organization organization, string token);
Task SendRequestSMAccessToAdminEmailAsync(IEnumerable<string> adminEmails, string organizationName, string userRequestingAccess, string emailContent); Task SendRequestSMAccessToAdminEmailAsync(IEnumerable<string> adminEmails, string organizationName, string userRequestingAccess, string emailContent);
#nullable disable
Task SendFamiliesForEnterpriseRemoveSponsorshipsEmailAsync(string email, string offerAcceptanceDate, string organizationId, Task SendFamiliesForEnterpriseRemoveSponsorshipsEmailAsync(string email, string offerAcceptanceDate, string organizationId,
string organizationName); string organizationName);
#nullable enable
Task SendClaimedDomainUserEmailAsync(ClaimedUserDomainClaimedEmails emailList); Task SendClaimedDomainUserEmailAsync(ClaimedUserDomainClaimedEmails emailList);
Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable<string> adminEmails, Guid organizationId, string email, string userName); Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable<string> adminEmails, Guid organizationId, string email, string? userName);
Task SendBulkSecurityTaskNotificationsAsync(Organization org, IEnumerable<UserSecurityTasksCount> securityTaskNotifications, IEnumerable<string> adminOwnerEmails); Task SendBulkSecurityTaskNotificationsAsync(Organization org, IEnumerable<UserSecurityTasksCount> securityTaskNotifications, IEnumerable<string> adminOwnerEmails);
} }

View File

@ -1,4 +1,6 @@
using Amazon; #nullable enable
using Amazon;
using Amazon.SimpleEmail; using Amazon.SimpleEmail;
using Amazon.SimpleEmail.Model; using Amazon.SimpleEmail.Model;
using Bit.Core.Models.Mail; using Bit.Core.Models.Mail;
@ -17,7 +19,7 @@ public class AmazonSesMailDeliveryService : IMailDeliveryService, IDisposable
private readonly IAmazonSimpleEmailService _client; private readonly IAmazonSimpleEmailService _client;
private readonly string _source; private readonly string _source;
private readonly string _senderTag; private readonly string _senderTag;
private readonly string _configSetName; private readonly string? _configSetName;
public AmazonSesMailDeliveryService( public AmazonSesMailDeliveryService(
GlobalSettings globalSettings, GlobalSettings globalSettings,

View File

@ -1,4 +1,6 @@
using System.Text; #nullable enable
using System.Text;
using System.Text.Json; using System.Text.Json;
using Azure.Storage.Queues; using Azure.Storage.Queues;
using Bit.Core.Utilities; using Bit.Core.Utilities;

View File

@ -1,4 +1,6 @@
using Bit.Core.Context; #nullable enable
using Bit.Core.Context;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
@ -34,8 +36,8 @@ public class CollectionService : ICollectionService
_currentContext = currentContext; _currentContext = currentContext;
} }
public async Task SaveAsync(Collection collection, IEnumerable<CollectionAccessSelection> groups = null, public async Task SaveAsync(Collection collection, IEnumerable<CollectionAccessSelection>? groups = null,
IEnumerable<CollectionAccessSelection> users = null) IEnumerable<CollectionAccessSelection>? users = null)
{ {
var org = await _organizationRepository.GetByIdAsync(collection.OrganizationId); var org = await _organizationRepository.GetByIdAsync(collection.OrganizationId);
if (org == null) if (org == null)

View File

@ -1,4 +1,7 @@
using System.Net; #nullable enable
using System.Diagnostics;
using System.Net;
using System.Reflection; using System.Reflection;
using System.Text.Json; using System.Text.Json;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
@ -213,6 +216,7 @@ public class HandlebarsMailService : IMailService
public async Task SendOrganizationAutoscaledEmailAsync(Organization organization, int initialSeatCount, IEnumerable<string> ownerEmails) public async Task SendOrganizationAutoscaledEmailAsync(Organization organization, int initialSeatCount, IEnumerable<string> ownerEmails)
{ {
Debug.Assert(organization.Seats.HasValue, "Organization is expected to have a non-null value for seats at the time of sending this email");
var message = CreateDefaultMessage($"{organization.DisplayName()} Seat Count Has Increased", ownerEmails); var message = CreateDefaultMessage($"{organization.DisplayName()} Seat Count Has Increased", ownerEmails);
var model = new OrganizationSeatsAutoscaledViewModel var model = new OrganizationSeatsAutoscaledViewModel
{ {
@ -286,7 +290,7 @@ public class HandlebarsMailService : IMailService
var messageModels = orgInvitesInfo.OrgUserTokenPairs.Select(orgUserTokenPair => var messageModels = orgInvitesInfo.OrgUserTokenPairs.Select(orgUserTokenPair =>
{ {
Debug.Assert(orgUserTokenPair.OrgUser.Email is not null);
var orgUserInviteViewModel = OrganizationUserInvitedViewModel.CreateFromInviteInfo( var orgUserInviteViewModel = OrganizationUserInvitedViewModel.CreateFromInviteInfo(
orgInvitesInfo, orgUserTokenPair.OrgUser, orgUserTokenPair.Token, _globalSettings); orgInvitesInfo, orgUserTokenPair.OrgUser, orgUserTokenPair.Token, _globalSettings);
return CreateMessage(orgUserTokenPair.OrgUser.Email, orgUserInviteViewModel); return CreateMessage(orgUserTokenPair.OrgUser.Email, orgUserInviteViewModel);
@ -358,7 +362,7 @@ public class HandlebarsMailService : IMailService
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash, WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
SiteName = _globalSettings.SiteName, SiteName = _globalSettings.SiteName,
ProviderId = provider.Id, ProviderId = provider.Id,
ProviderName = CoreHelpers.SanitizeForEmail(provider.DisplayName(), false), ProviderName = CoreHelpers.SanitizeForEmail(provider.DisplayName()!, false),
ProviderNameUrlEncoded = WebUtility.UrlEncode(provider.Name), ProviderNameUrlEncoded = WebUtility.UrlEncode(provider.Name),
ProviderBillingEmail = provider.BillingEmail, ProviderBillingEmail = provider.BillingEmail,
ProviderCreationDate = provider.CreationDate.ToLongDateString(), ProviderCreationDate = provider.CreationDate.ToLongDateString(),
@ -448,7 +452,7 @@ public class HandlebarsMailService : IMailService
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
public async Task SendLicenseExpiredAsync(IEnumerable<string> emails, string organizationName = null) public async Task SendLicenseExpiredAsync(IEnumerable<string> emails, string? organizationName = null)
{ {
var message = CreateDefaultMessage("License Expired", emails); var message = CreateDefaultMessage("License Expired", emails);
var model = new LicenseExpiredViewModel(); var model = new LicenseExpiredViewModel();
@ -598,12 +602,14 @@ public class HandlebarsMailService : IMailService
} }
private async Task AddMessageContentAsync<T>(MailMessage message, string templateName, T model) private async Task AddMessageContentAsync<T>(MailMessage message, string templateName, T model)
where T : notnull
{ {
message.HtmlContent = await RenderAsync($"{templateName}.html", model); message.HtmlContent = await RenderAsync($"{templateName}.html", model);
message.TextContent = await RenderAsync($"{templateName}.text", model); message.TextContent = await RenderAsync($"{templateName}.text", model);
} }
private async Task<string> RenderAsync<T>(string templateName, T model) private async Task<string?> RenderAsync<T>(string templateName, T model)
where T : notnull
{ {
await RegisterHelpersAndPartialsAsync(); await RegisterHelpersAndPartialsAsync();
if (!_templateCache.TryGetValue(templateName, out var template)) if (!_templateCache.TryGetValue(templateName, out var template))
@ -618,7 +624,7 @@ public class HandlebarsMailService : IMailService
return template != null ? template(model) : null; return template != null ? template(model) : null;
} }
private async Task<string> ReadSourceAsync(string templateName) private async Task<string?> ReadSourceAsync(string templateName)
{ {
var assembly = typeof(HandlebarsMailService).GetTypeInfo().Assembly; var assembly = typeof(HandlebarsMailService).GetTypeInfo().Assembly;
var fullTemplateName = $"{Namespace}.{templateName}.hbs"; var fullTemplateName = $"{Namespace}.{templateName}.hbs";
@ -626,7 +632,7 @@ public class HandlebarsMailService : IMailService
{ {
return null; return null;
} }
using (var s = assembly.GetManifestResourceStream(fullTemplateName)) using (var s = assembly.GetManifestResourceStream(fullTemplateName)!)
using (var sr = new StreamReader(s)) using (var sr = new StreamReader(s))
{ {
return await sr.ReadToEndAsync(); return await sr.ReadToEndAsync();
@ -757,7 +763,7 @@ public class HandlebarsMailService : IMailService
var emailList = new List<string>(); var emailList = new List<string>();
if (parameters[0] is JsonElement jsonElement && jsonElement.ValueKind == JsonValueKind.Array) if (parameters[0] is JsonElement jsonElement && jsonElement.ValueKind == JsonValueKind.Array)
{ {
emailList = jsonElement.EnumerateArray().Select(e => e.GetString()).ToList(); emailList = jsonElement.EnumerateArray().Select(e => e.GetString()!).ToList();
} }
else if (parameters[0] is IEnumerable<string> emails) else if (parameters[0] is IEnumerable<string> emails)
{ {
@ -1276,7 +1282,7 @@ public class HandlebarsMailService : IMailService
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
public async Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable<string> adminEmails, Guid organizationId, string email, string userName) public async Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable<string> adminEmails, Guid organizationId, string email, string? userName)
{ {
var templateName = _globalSettings.SelfHosted ? var templateName = _globalSettings.SelfHosted ?
"AdminConsole.SelfHostNotifyAdminDeviceApprovalRequested" : "AdminConsole.SelfHostNotifyAdminDeviceApprovalRequested" :
@ -1313,7 +1319,7 @@ public class HandlebarsMailService : IMailService
await EnqueueMailAsync(messageModels.ToList()); await EnqueueMailAsync(messageModels.ToList());
} }
private static string GetUserIdentifier(string email, string userName) private static string GetUserIdentifier(string email, string? userName)
{ {
return string.IsNullOrEmpty(userName) ? email : CoreHelpers.SanitizeForEmail(userName, false); return string.IsNullOrEmpty(userName) ? email : CoreHelpers.SanitizeForEmail(userName, false);
} }

View File

@ -1,4 +1,5 @@
using System.Reflection; #nullable enable
using Bit.Core.Resources; using Bit.Core.Resources;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
@ -10,8 +11,8 @@ public class I18nService : II18nService
public I18nService(IStringLocalizerFactory factory) public I18nService(IStringLocalizerFactory factory)
{ {
var assemblyName = new AssemblyName(typeof(SharedResources).GetTypeInfo().Assembly.FullName); var assemblyName = typeof(SharedResources).Assembly.GetName()!;
_localizer = factory.Create("SharedResources", assemblyName.Name); _localizer = factory.Create("SharedResources", assemblyName.Name!);
} }
public LocalizedString GetLocalizedHtmlString(string key) public LocalizedString GetLocalizedHtmlString(string key)

View File

@ -1,4 +1,5 @@
using System.Reflection; #nullable enable
using Bit.Core.Resources; using Bit.Core.Resources;
using Microsoft.AspNetCore.Mvc.Localization; using Microsoft.AspNetCore.Mvc.Localization;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
@ -13,9 +14,9 @@ public class I18nViewLocalizer : IViewLocalizer
public I18nViewLocalizer(IStringLocalizerFactory stringFactory, public I18nViewLocalizer(IStringLocalizerFactory stringFactory,
IHtmlLocalizerFactory htmlFactory) IHtmlLocalizerFactory htmlFactory)
{ {
var assemblyName = new AssemblyName(typeof(SharedResources).GetTypeInfo().Assembly.FullName); var assemblyName = typeof(SharedResources).Assembly.GetName()!;
_stringLocalizer = stringFactory.Create("SharedResources", assemblyName.Name); _stringLocalizer = stringFactory.Create("SharedResources", assemblyName.Name!);
_htmlLocalizer = htmlFactory.Create("SharedResources", assemblyName.Name); _htmlLocalizer = htmlFactory.Create("SharedResources", assemblyName.Name!);
} }
public LocalizedHtmlString this[string name] => _htmlLocalizer[name]; public LocalizedHtmlString this[string name] => _htmlLocalizer[name];

View File

@ -22,12 +22,17 @@ public class MailKitSmtpMailDeliveryService : IMailDeliveryService
ILogger<MailKitSmtpMailDeliveryService> logger, ILogger<MailKitSmtpMailDeliveryService> logger,
IOptions<X509ChainOptions> x509ChainOptions) IOptions<X509ChainOptions> x509ChainOptions)
{ {
if (globalSettings.Mail?.Smtp?.Host == null) if (globalSettings.Mail.Smtp?.Host == null)
{ {
throw new ArgumentNullException(nameof(globalSettings.Mail.Smtp.Host)); throw new ArgumentNullException(nameof(globalSettings.Mail.Smtp.Host));
} }
_replyEmail = CoreHelpers.PunyEncode(globalSettings.Mail?.ReplyToEmail); if (globalSettings.Mail.ReplyToEmail == null)
{
throw new InvalidOperationException("A GlobalSettings.Mail.ReplyToEmail is required to be set up.");
}
_replyEmail = CoreHelpers.PunyEncode(globalSettings.Mail.ReplyToEmail);
if (_replyEmail.Contains("@")) if (_replyEmail.Contains("@"))
{ {

View File

@ -1,4 +1,6 @@
using System.Security.Claims; #nullable enable
using System.Security.Claims;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Models.Business; using Bit.Core.Models.Business;
@ -45,28 +47,28 @@ public class NoopLicensingService : ILicensingService
return new byte[0]; return new byte[0];
} }
public Task<OrganizationLicense> ReadOrganizationLicenseAsync(Organization organization) public Task<OrganizationLicense?> ReadOrganizationLicenseAsync(Organization organization)
{ {
return Task.FromResult<OrganizationLicense>(null); return Task.FromResult<OrganizationLicense?>(null);
} }
public Task<OrganizationLicense> ReadOrganizationLicenseAsync(Guid organizationId) public Task<OrganizationLicense?> ReadOrganizationLicenseAsync(Guid organizationId)
{ {
return Task.FromResult<OrganizationLicense>(null); return Task.FromResult<OrganizationLicense?>(null);
} }
public ClaimsPrincipal GetClaimsPrincipalFromLicense(ILicense license) public ClaimsPrincipal? GetClaimsPrincipalFromLicense(ILicense license)
{ {
return null; return null;
} }
public Task<string> CreateOrganizationTokenAsync(Organization organization, Guid installationId, SubscriptionInfo subscriptionInfo) public Task<string?> CreateOrganizationTokenAsync(Organization organization, Guid installationId, SubscriptionInfo subscriptionInfo)
{ {
return Task.FromResult<string>(null); return Task.FromResult<string?>(null);
} }
public Task<string> CreateUserTokenAsync(User user, SubscriptionInfo subscriptionInfo) public Task<string?> CreateUserTokenAsync(User user, SubscriptionInfo subscriptionInfo)
{ {
return Task.FromResult<string>(null); return Task.FromResult<string?>(null);
} }
} }

View File

@ -1,4 +1,6 @@
using Bit.Core.AdminConsole.Entities; #nullable enable
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.Auth.Entities; using Bit.Core.Auth.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
@ -137,7 +139,7 @@ public class NoopMailService : IMailService
return Task.FromResult(0); return Task.FromResult(0);
} }
public Task SendLicenseExpiredAsync(IEnumerable<string> emails, string organizationName = null) public Task SendLicenseExpiredAsync(IEnumerable<string> emails, string? organizationName = null)
{ {
return Task.FromResult(0); return Task.FromResult(0);
} }
@ -324,7 +326,7 @@ public class NoopMailService : IMailService
} }
public Task SendClaimedDomainUserEmailAsync(ClaimedUserDomainClaimedEmails emailList) => Task.CompletedTask; public Task SendClaimedDomainUserEmailAsync(ClaimedUserDomainClaimedEmails emailList) => Task.CompletedTask;
public Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable<string> adminEmails, Guid organizationId, string email, string userName) public Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable<string> adminEmails, Guid organizationId, string email, string? userName)
{ {
return Task.FromResult(0); return Task.FromResult(0);
} }

View File

@ -1,4 +1,7 @@
using System.Globalization; #nullable enable
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
@ -116,11 +119,11 @@ public static class CoreHelpers
return Regex.Replace(thumbprint, @"[^\da-fA-F]", string.Empty).ToUpper(); return Regex.Replace(thumbprint, @"[^\da-fA-F]", string.Empty).ToUpper();
} }
public static X509Certificate2 GetCertificate(string thumbprint) public static X509Certificate2? GetCertificate(string thumbprint)
{ {
thumbprint = CleanCertificateThumbprint(thumbprint); thumbprint = CleanCertificateThumbprint(thumbprint);
X509Certificate2 cert = null; X509Certificate2? cert = null;
var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser); var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly); certStore.Open(OpenFlags.ReadOnly);
var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false); var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
@ -141,7 +144,7 @@ public static class CoreHelpers
public async static Task<X509Certificate2> GetEmbeddedCertificateAsync(string file, string password) public async static Task<X509Certificate2> GetEmbeddedCertificateAsync(string file, string password)
{ {
var assembly = typeof(CoreHelpers).GetTypeInfo().Assembly; var assembly = typeof(CoreHelpers).GetTypeInfo().Assembly;
using (var s = assembly.GetManifestResourceStream($"Bit.Core.{file}")) using (var s = assembly.GetManifestResourceStream($"Bit.Core.{file}")!)
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
{ {
await s.CopyToAsync(ms); await s.CopyToAsync(ms);
@ -153,14 +156,14 @@ public static class CoreHelpers
{ {
var assembly = Assembly.GetCallingAssembly(); var assembly = Assembly.GetCallingAssembly();
var resourceName = assembly.GetManifestResourceNames().Single(n => n.EndsWith(file)); var resourceName = assembly.GetManifestResourceNames().Single(n => n.EndsWith(file));
using (var stream = assembly.GetManifestResourceStream(resourceName)) using (var stream = assembly.GetManifestResourceStream(resourceName)!)
using (var reader = new StreamReader(stream)) using (var reader = new StreamReader(stream))
{ {
return reader.ReadToEnd(); return reader.ReadToEnd();
} }
} }
public async static Task<X509Certificate2> GetBlobCertificateAsync(string connectionString, string container, string file, string password) public async static Task<X509Certificate2?> GetBlobCertificateAsync(string connectionString, string container, string file, string password)
{ {
try try
{ {
@ -233,7 +236,7 @@ public static class CoreHelpers
throw new ArgumentOutOfRangeException(nameof(length), "length cannot be less than zero."); throw new ArgumentOutOfRangeException(nameof(length), "length cannot be less than zero.");
} }
if ((characters?.Length ?? 0) == 0) if (string.IsNullOrEmpty(characters))
{ {
throw new ArgumentOutOfRangeException(nameof(characters), "characters invalid."); throw new ArgumentOutOfRangeException(nameof(characters), "characters invalid.");
} }
@ -346,10 +349,10 @@ public static class CoreHelpers
/// </summary> /// </summary>
public static T CloneObject<T>(T obj) public static T CloneObject<T>(T obj)
{ {
return JsonSerializer.Deserialize<T>(JsonSerializer.Serialize(obj)); return JsonSerializer.Deserialize<T>(JsonSerializer.Serialize(obj))!;
} }
public static bool SettingHasValue(string setting) public static bool SettingHasValue([NotNullWhen(true)] string? setting)
{ {
var normalizedSetting = setting?.ToLowerInvariant(); var normalizedSetting = setting?.ToLowerInvariant();
return !string.IsNullOrWhiteSpace(normalizedSetting) && !normalizedSetting.Equals("secret") && return !string.IsNullOrWhiteSpace(normalizedSetting) && !normalizedSetting.Equals("secret") &&
@ -448,7 +451,8 @@ public static class CoreHelpers
return output; return output;
} }
public static string PunyEncode(string text) [return: NotNullIfNotNull(nameof(text))]
public static string? PunyEncode(string? text)
{ {
if (text == "") if (text == "")
{ {
@ -473,21 +477,21 @@ public static class CoreHelpers
} }
} }
public static string FormatLicenseSignatureValue(object val) public static string? FormatLicenseSignatureValue(object val)
{ {
if (val == null) if (val == null)
{ {
return string.Empty; return string.Empty;
} }
if (val.GetType() == typeof(DateTime)) if (val is DateTime dateTimeVal)
{ {
return ToEpocSeconds((DateTime)val).ToString(); return ToEpocSeconds(dateTimeVal).ToString();
} }
if (val.GetType() == typeof(bool)) if (val is bool boolVal)
{ {
return val.ToString().ToLowerInvariant(); return boolVal.ToString().ToLowerInvariant();
} }
if (val is PlanType planType) if (val is PlanType planType)
@ -625,7 +629,7 @@ public static class CoreHelpers
return subName; return subName;
} }
public static string GetIpAddress(this Microsoft.AspNetCore.Http.HttpContext httpContext, public static string? GetIpAddress(this Microsoft.AspNetCore.Http.HttpContext httpContext,
GlobalSettings globalSettings) GlobalSettings globalSettings)
{ {
if (httpContext == null) if (httpContext == null)
@ -652,7 +656,7 @@ public static class CoreHelpers
(!globalSettings.SelfHosted && origin == "https://bitwarden.com"); (!globalSettings.SelfHosted && origin == "https://bitwarden.com");
} }
public static X509Certificate2 GetIdentityServerCertificate(GlobalSettings globalSettings) public static X509Certificate2? GetIdentityServerCertificate(GlobalSettings globalSettings)
{ {
if (globalSettings.SelfHosted && if (globalSettings.SelfHosted &&
SettingHasValue(globalSettings.IdentityServer.CertificatePassword) SettingHasValue(globalSettings.IdentityServer.CertificatePassword)
@ -804,14 +808,16 @@ public static class CoreHelpers
/// <param name="jsonData">The JSON data</param> /// <param name="jsonData">The JSON data</param>
/// <typeparam name="T">The type to deserialize into</typeparam> /// <typeparam name="T">The type to deserialize into</typeparam>
/// <returns></returns> /// <returns></returns>
public static T LoadClassFromJsonData<T>(string jsonData) where T : new() public static T LoadClassFromJsonData<T>(string? jsonData) where T : new()
{ {
if (string.IsNullOrWhiteSpace(jsonData)) if (string.IsNullOrWhiteSpace(jsonData))
{ {
return new T(); return new T();
} }
#nullable disable // TODO: Remove this and fix any callee warnings.
return System.Text.Json.JsonSerializer.Deserialize<T>(jsonData, _jsonSerializerOptions); return System.Text.Json.JsonSerializer.Deserialize<T>(jsonData, _jsonSerializerOptions);
#nullable enable
} }
public static string ClassToJsonData<T>(T data) public static string ClassToJsonData<T>(T data)
@ -829,7 +835,7 @@ public static class CoreHelpers
return list; return list;
} }
public static string DecodeMessageText(this QueueMessage message) public static string? DecodeMessageText(this QueueMessage message)
{ {
var text = message?.MessageText; var text = message?.MessageText;
if (string.IsNullOrWhiteSpace(text)) if (string.IsNullOrWhiteSpace(text))
@ -852,7 +858,7 @@ public static class CoreHelpers
Encoding.UTF8.GetBytes(input1), Encoding.UTF8.GetBytes(input2)); Encoding.UTF8.GetBytes(input1), Encoding.UTF8.GetBytes(input2));
} }
public static string ObfuscateEmail(string email) public static string? ObfuscateEmail(string email)
{ {
if (email == null) if (email == null)
{ {
@ -886,7 +892,7 @@ public static class CoreHelpers
} }
public static string GetEmailDomain(string email) public static string? GetEmailDomain(string email)
{ {
if (!string.IsNullOrWhiteSpace(email)) if (!string.IsNullOrWhiteSpace(email))
{ {
@ -906,7 +912,7 @@ public static class CoreHelpers
return _whiteSpaceRegex.Replace(input, newValue); return _whiteSpaceRegex.Replace(input, newValue);
} }
public static string RedactEmailAddress(string email) public static string? RedactEmailAddress(string email)
{ {
if (string.IsNullOrWhiteSpace(email)) if (string.IsNullOrWhiteSpace(email))
{ {

View File

@ -513,15 +513,21 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
} }
} }
public async Task ReplaceAsync(Core.Entities.Collection collection, IEnumerable<CollectionAccessSelection> groups, public async Task ReplaceAsync(Core.Entities.Collection collection, IEnumerable<CollectionAccessSelection>? groups,
IEnumerable<CollectionAccessSelection> users) IEnumerable<CollectionAccessSelection>? users)
{ {
await UpsertAsync(collection); await UpsertAsync(collection);
using (var scope = ServiceScopeFactory.CreateScope()) using (var scope = ServiceScopeFactory.CreateScope())
{ {
var dbContext = GetDatabaseContext(scope); var dbContext = GetDatabaseContext(scope);
await ReplaceCollectionGroupsAsync(dbContext, collection, groups); if (groups != null)
await ReplaceCollectionUsersAsync(dbContext, collection, users); {
await ReplaceCollectionGroupsAsync(dbContext, collection, groups);
}
if (users != null)
{
await ReplaceCollectionUsersAsync(dbContext, collection, users);
}
await dbContext.UserBumpAccountRevisionDateByCollectionIdAsync(collection.Id, collection.OrganizationId); await dbContext.UserBumpAccountRevisionDateByCollectionIdAsync(collection.Id, collection.OrganizationId);
await dbContext.SaveChangesAsync(); await dbContext.SaveChangesAsync();
} }