From 7c9cf66ac3ce0ce53b51d39ef3031a864bbb9ff7 Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Fri, 11 Apr 2025 13:57:52 -0400 Subject: [PATCH 1/3] Nullable Platform & Unowned Services --- .../IUpdateInstallationCommand.cs | 4 +- .../UpdateInstallationCommand.cs | 4 +- .../GetInstallationQuery.cs | 6 ++- .../IGetInstallationQuery.cs | 6 ++- .../PlatformServiceCollectionExtensions.cs | 4 +- .../Push/Services/IPushRegistrationService.cs | 4 +- .../Services/NoopPushRegistrationService.cs | 4 +- .../Services/RelayPushRegistrationService.cs | 4 +- .../Repositories/ICollectionRepository.cs | 2 +- src/Core/Services/ILicensingService.cs | 14 +++--- src/Core/Services/IMailService.cs | 6 ++- .../AmazonSesMailDeliveryService.cs | 6 ++- .../Implementations/AzureQueueService.cs | 4 +- .../Implementations/CollectionService.cs | 8 ++-- .../Implementations/HandlebarsMailService.cs | 20 +++++--- .../Services/Implementations/I18nService.cs | 7 +-- .../Implementations/I18nViewLocalizer.cs | 9 ++-- .../MailKitSmtpMailDeliveryService.cs | 11 +++-- .../NoopLicensingService.cs | 22 +++++---- .../NoopImplementations/NoopMailService.cs | 6 ++- src/Core/Utilities/CoreHelpers.cs | 48 +++++++++++-------- .../Repositories/CollectionRepository.cs | 4 +- 22 files changed, 126 insertions(+), 77 deletions(-) diff --git a/src/Core/Platform/Installations/Commands/UpdateInstallationActivityDateCommand/IUpdateInstallationCommand.cs b/src/Core/Platform/Installations/Commands/UpdateInstallationActivityDateCommand/IUpdateInstallationCommand.cs index d0c25b96a455..02263bba4092 100644 --- a/src/Core/Platform/Installations/Commands/UpdateInstallationActivityDateCommand/IUpdateInstallationCommand.cs +++ b/src/Core/Platform/Installations/Commands/UpdateInstallationActivityDateCommand/IUpdateInstallationCommand.cs @@ -1,4 +1,6 @@ -namespace Bit.Core.Platform.Installations; +#nullable enable + +namespace Bit.Core.Platform.Installations; /// /// Command interface responsible for updating data on an `Installation` diff --git a/src/Core/Platform/Installations/Commands/UpdateInstallationActivityDateCommand/UpdateInstallationCommand.cs b/src/Core/Platform/Installations/Commands/UpdateInstallationActivityDateCommand/UpdateInstallationCommand.cs index 4b0bc3bbe804..69667cb4acf9 100644 --- a/src/Core/Platform/Installations/Commands/UpdateInstallationActivityDateCommand/UpdateInstallationCommand.cs +++ b/src/Core/Platform/Installations/Commands/UpdateInstallationActivityDateCommand/UpdateInstallationCommand.cs @@ -1,4 +1,6 @@ -namespace Bit.Core.Platform.Installations; +#nullable enable + +namespace Bit.Core.Platform.Installations; /// /// Commands responsible for updating an installation from diff --git a/src/Core/Platform/Installations/Queries/GetInstallationQuery/GetInstallationQuery.cs b/src/Core/Platform/Installations/Queries/GetInstallationQuery/GetInstallationQuery.cs index b0d87458006d..2afb319a1f5d 100644 --- a/src/Core/Platform/Installations/Queries/GetInstallationQuery/GetInstallationQuery.cs +++ b/src/Core/Platform/Installations/Queries/GetInstallationQuery/GetInstallationQuery.cs @@ -1,4 +1,6 @@ -namespace Bit.Core.Platform.Installations; +#nullable enable + +namespace Bit.Core.Platform.Installations; /// /// Queries responsible for fetching an installation from @@ -19,7 +21,7 @@ public GetInstallationQuery(IInstallationRepository installationRepository) } /// - public async Task GetByIdAsync(Guid installationId) + public async Task GetByIdAsync(Guid installationId) { if (installationId == default(Guid)) { diff --git a/src/Core/Platform/Installations/Queries/GetInstallationQuery/IGetInstallationQuery.cs b/src/Core/Platform/Installations/Queries/GetInstallationQuery/IGetInstallationQuery.cs index 9615cf986d27..10bcfba9b8f5 100644 --- a/src/Core/Platform/Installations/Queries/GetInstallationQuery/IGetInstallationQuery.cs +++ b/src/Core/Platform/Installations/Queries/GetInstallationQuery/IGetInstallationQuery.cs @@ -1,4 +1,6 @@ -namespace Bit.Core.Platform.Installations; +#nullable enable + +namespace Bit.Core.Platform.Installations; /// /// Query interface responsible for fetching an installation from @@ -16,5 +18,5 @@ public interface IGetInstallationQuery /// The GUID id of the installation. /// A task containing an `Installation`. /// - Task GetByIdAsync(Guid installationId); + Task GetByIdAsync(Guid installationId); } diff --git a/src/Core/Platform/PlatformServiceCollectionExtensions.cs b/src/Core/Platform/PlatformServiceCollectionExtensions.cs index bba0b0aeddec..d426b934c858 100644 --- a/src/Core/Platform/PlatformServiceCollectionExtensions.cs +++ b/src/Core/Platform/PlatformServiceCollectionExtensions.cs @@ -1,4 +1,6 @@ -using Bit.Core.Platform.Installations; +#nullable enable + +using Bit.Core.Platform.Installations; using Microsoft.Extensions.DependencyInjection; namespace Bit.Core.Platform; diff --git a/src/Core/Platform/Push/Services/IPushRegistrationService.cs b/src/Core/Platform/Push/Services/IPushRegistrationService.cs index 469cd2577bb3..7dff9fd2f27a 100644 --- a/src/Core/Platform/Push/Services/IPushRegistrationService.cs +++ b/src/Core/Platform/Push/Services/IPushRegistrationService.cs @@ -1,4 +1,6 @@ -using Bit.Core.Enums; +#nullable enable + +using Bit.Core.Enums; using Bit.Core.NotificationHub; namespace Bit.Core.Platform.Push; diff --git a/src/Core/Platform/Push/Services/NoopPushRegistrationService.cs b/src/Core/Platform/Push/Services/NoopPushRegistrationService.cs index 9a7674232ad3..7eadb834e99c 100644 --- a/src/Core/Platform/Push/Services/NoopPushRegistrationService.cs +++ b/src/Core/Platform/Push/Services/NoopPushRegistrationService.cs @@ -1,4 +1,6 @@ -using Bit.Core.Enums; +#nullable enable + +using Bit.Core.Enums; using Bit.Core.NotificationHub; namespace Bit.Core.Platform.Push.Internal; diff --git a/src/Core/Platform/Push/Services/RelayPushRegistrationService.cs b/src/Core/Platform/Push/Services/RelayPushRegistrationService.cs index 1a3843d05a6d..20e405935bad 100644 --- a/src/Core/Platform/Push/Services/RelayPushRegistrationService.cs +++ b/src/Core/Platform/Push/Services/RelayPushRegistrationService.cs @@ -1,4 +1,6 @@ -using Bit.Core.Enums; +#nullable enable + +using Bit.Core.Enums; using Bit.Core.IdentityServer; using Bit.Core.Models.Api; using Bit.Core.NotificationHub; diff --git a/src/Core/Repositories/ICollectionRepository.cs b/src/Core/Repositories/ICollectionRepository.cs index fc3a6715ac2a..1d41a6ee1fc6 100644 --- a/src/Core/Repositories/ICollectionRepository.cs +++ b/src/Core/Repositories/ICollectionRepository.cs @@ -48,7 +48,7 @@ public interface ICollectionRepository : IRepository Task GetByIdWithPermissionsAsync(Guid collectionId, Guid? userId, bool includeAccessRelationships); Task CreateAsync(Collection obj, IEnumerable? groups, IEnumerable? users); - Task ReplaceAsync(Collection obj, IEnumerable groups, IEnumerable users); + Task ReplaceAsync(Collection obj, IEnumerable? groups, IEnumerable? users); Task DeleteUserAsync(Guid collectionId, Guid organizationUserId); Task UpdateUsersAsync(Guid id, IEnumerable users); Task> GetManyUsersByIdAsync(Guid id); diff --git a/src/Core/Services/ILicensingService.cs b/src/Core/Services/ILicensingService.cs index 7301f7c6898e..2115e430857d 100644 --- a/src/Core/Services/ILicensingService.cs +++ b/src/Core/Services/ILicensingService.cs @@ -1,4 +1,6 @@ -using System.Security.Claims; +#nullable enable + +using System.Security.Claims; using Bit.Core.AdminConsole.Entities; using Bit.Core.Entities; using Bit.Core.Models.Business; @@ -12,14 +14,14 @@ public interface ILicensingService Task ValidateUserPremiumAsync(User user); bool VerifyLicense(ILicense license); byte[] SignLicense(ILicense license); - Task ReadOrganizationLicenseAsync(Organization organization); - Task ReadOrganizationLicenseAsync(Guid organizationId); - ClaimsPrincipal GetClaimsPrincipalFromLicense(ILicense license); + Task ReadOrganizationLicenseAsync(Organization organization); + Task ReadOrganizationLicenseAsync(Guid organizationId); + ClaimsPrincipal? GetClaimsPrincipalFromLicense(ILicense license); - Task CreateOrganizationTokenAsync( + Task CreateOrganizationTokenAsync( Organization organization, Guid installationId, SubscriptionInfo subscriptionInfo); - Task CreateUserTokenAsync(User user, SubscriptionInfo subscriptionInfo); + Task CreateUserTokenAsync(User user, SubscriptionInfo subscriptionInfo); } diff --git a/src/Core/Services/IMailService.cs b/src/Core/Services/IMailService.cs index e61127c57a3b..52724721ee68 100644 --- a/src/Core/Services/IMailService.cs +++ b/src/Core/Services/IMailService.cs @@ -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.Auth.Entities; using Bit.Core.Billing.Enums; @@ -55,7 +57,7 @@ Task SendInvoiceUpcoming( bool mentionInvoices); Task SendPaymentFailedAsync(string email, decimal amount, bool mentionInvoices); Task SendAddedCreditAsync(string email, decimal amount); - Task SendLicenseExpiredAsync(IEnumerable emails, string organizationName = null); + Task SendLicenseExpiredAsync(IEnumerable emails, string? organizationName = null); Task SendNewDeviceLoggedInEmail(string email, string deviceType, DateTime timestamp, string ip); Task SendRecoverTwoFactorEmail(string email, DateTime timestamp, string ip); Task SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(string organizationName, string email); diff --git a/src/Core/Services/Implementations/AmazonSesMailDeliveryService.cs b/src/Core/Services/Implementations/AmazonSesMailDeliveryService.cs index adf406cf0b01..344c2e712d4b 100644 --- a/src/Core/Services/Implementations/AmazonSesMailDeliveryService.cs +++ b/src/Core/Services/Implementations/AmazonSesMailDeliveryService.cs @@ -1,4 +1,6 @@ -using Amazon; +#nullable enable + +using Amazon; using Amazon.SimpleEmail; using Amazon.SimpleEmail.Model; using Bit.Core.Models.Mail; @@ -17,7 +19,7 @@ public class AmazonSesMailDeliveryService : IMailDeliveryService, IDisposable private readonly IAmazonSimpleEmailService _client; private readonly string _source; private readonly string _senderTag; - private readonly string _configSetName; + private readonly string? _configSetName; public AmazonSesMailDeliveryService( GlobalSettings globalSettings, diff --git a/src/Core/Services/Implementations/AzureQueueService.cs b/src/Core/Services/Implementations/AzureQueueService.cs index 11c1a58ae305..ca95e96b64f7 100644 --- a/src/Core/Services/Implementations/AzureQueueService.cs +++ b/src/Core/Services/Implementations/AzureQueueService.cs @@ -1,4 +1,6 @@ -using System.Text; +#nullable enable + +using System.Text; using System.Text.Json; using Azure.Storage.Queues; using Bit.Core.Utilities; diff --git a/src/Core/Services/Implementations/CollectionService.cs b/src/Core/Services/Implementations/CollectionService.cs index f6e9735f4ec8..46853447d691 100644 --- a/src/Core/Services/Implementations/CollectionService.cs +++ b/src/Core/Services/Implementations/CollectionService.cs @@ -1,4 +1,6 @@ -using Bit.Core.Context; +#nullable enable + +using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Exceptions; using Bit.Core.Models.Data; @@ -34,8 +36,8 @@ public CollectionService( _currentContext = currentContext; } - public async Task SaveAsync(Collection collection, IEnumerable groups = null, - IEnumerable users = null) + public async Task SaveAsync(Collection collection, IEnumerable? groups = null, + IEnumerable? users = null) { var org = await _organizationRepository.GetByIdAsync(collection.OrganizationId); if (org == null) diff --git a/src/Core/Services/Implementations/HandlebarsMailService.cs b/src/Core/Services/Implementations/HandlebarsMailService.cs index a5513423248f..ae569263b1d6 100644 --- a/src/Core/Services/Implementations/HandlebarsMailService.cs +++ b/src/Core/Services/Implementations/HandlebarsMailService.cs @@ -1,4 +1,7 @@ -using System.Net; +#nullable enable + +using System.Diagnostics; +using System.Net; using System.Reflection; using System.Text.Json; using Bit.Core.AdminConsole.Entities; @@ -212,6 +215,7 @@ public async Task SendNoMasterPasswordHintEmailAsync(string email) public async Task SendOrganizationAutoscaledEmailAsync(Organization organization, int initialSeatCount, IEnumerable 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 model = new OrganizationSeatsAutoscaledViewModel { @@ -285,7 +289,7 @@ MailQueueMessage CreateMessage(string email, object model) var messageModels = orgInvitesInfo.OrgUserTokenPairs.Select(orgUserTokenPair => { - + Debug.Assert(orgUserTokenPair.OrgUser.Email is not null); var orgUserInviteViewModel = OrganizationUserInvitedViewModel.CreateFromInviteInfo( orgInvitesInfo, orgUserTokenPair.OrgUser, orgUserTokenPair.Token, _globalSettings); return CreateMessage(orgUserTokenPair.OrgUser.Email, orgUserInviteViewModel); @@ -357,7 +361,7 @@ public async Task SendInitiateDeletProviderEmailAsync(string email, Provider pro WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash, SiteName = _globalSettings.SiteName, ProviderId = provider.Id, - ProviderName = CoreHelpers.SanitizeForEmail(provider.DisplayName(), false), + ProviderName = CoreHelpers.SanitizeForEmail(provider.DisplayName()!, false), ProviderNameUrlEncoded = WebUtility.UrlEncode(provider.Name), ProviderBillingEmail = provider.BillingEmail, ProviderCreationDate = provider.CreationDate.ToLongDateString(), @@ -447,7 +451,7 @@ public async Task SendAddedCreditAsync(string email, decimal amount) await _mailDeliveryService.SendEmailAsync(message); } - public async Task SendLicenseExpiredAsync(IEnumerable emails, string organizationName = null) + public async Task SendLicenseExpiredAsync(IEnumerable emails, string? organizationName = null) { var message = CreateDefaultMessage("License Expired", emails); var model = new LicenseExpiredViewModel(); @@ -597,12 +601,14 @@ private static MailMessage CreateDefaultMessage(string subject, IEnumerable(MailMessage message, string templateName, T model) + where T : notnull { message.HtmlContent = await RenderAsync($"{templateName}.html", model); message.TextContent = await RenderAsync($"{templateName}.text", model); } - private async Task RenderAsync(string templateName, T model) + private async Task RenderAsync(string templateName, T model) + where T : notnull { await RegisterHelpersAndPartialsAsync(); if (!_templateCache.TryGetValue(templateName, out var template)) @@ -617,7 +623,7 @@ private async Task RenderAsync(string templateName, T model) return template != null ? template(model) : null; } - private async Task ReadSourceAsync(string templateName) + private async Task ReadSourceAsync(string templateName) { var assembly = typeof(HandlebarsMailService).GetTypeInfo().Assembly; var fullTemplateName = $"{Namespace}.{templateName}.hbs"; @@ -625,7 +631,7 @@ private async Task ReadSourceAsync(string templateName) { return null; } - using (var s = assembly.GetManifestResourceStream(fullTemplateName)) + using (var s = assembly.GetManifestResourceStream(fullTemplateName)!) using (var sr = new StreamReader(s)) { return await sr.ReadToEndAsync(); diff --git a/src/Core/Services/Implementations/I18nService.cs b/src/Core/Services/Implementations/I18nService.cs index 7d99dacbacaa..25e2f8e5dc4f 100644 --- a/src/Core/Services/Implementations/I18nService.cs +++ b/src/Core/Services/Implementations/I18nService.cs @@ -1,4 +1,5 @@ -using System.Reflection; +#nullable enable + using Bit.Core.Resources; using Microsoft.Extensions.Localization; @@ -10,8 +11,8 @@ public class I18nService : II18nService public I18nService(IStringLocalizerFactory factory) { - var assemblyName = new AssemblyName(typeof(SharedResources).GetTypeInfo().Assembly.FullName); - _localizer = factory.Create("SharedResources", assemblyName.Name); + var assemblyName = typeof(SharedResources).Assembly.GetName()!; + _localizer = factory.Create("SharedResources", assemblyName.Name!); } public LocalizedString GetLocalizedHtmlString(string key) diff --git a/src/Core/Services/Implementations/I18nViewLocalizer.cs b/src/Core/Services/Implementations/I18nViewLocalizer.cs index 69699d9c4b67..7f41782c5339 100644 --- a/src/Core/Services/Implementations/I18nViewLocalizer.cs +++ b/src/Core/Services/Implementations/I18nViewLocalizer.cs @@ -1,4 +1,5 @@ -using System.Reflection; +#nullable enable + using Bit.Core.Resources; using Microsoft.AspNetCore.Mvc.Localization; using Microsoft.Extensions.Localization; @@ -13,9 +14,9 @@ public class I18nViewLocalizer : IViewLocalizer public I18nViewLocalizer(IStringLocalizerFactory stringFactory, IHtmlLocalizerFactory htmlFactory) { - var assemblyName = new AssemblyName(typeof(SharedResources).GetTypeInfo().Assembly.FullName); - _stringLocalizer = stringFactory.Create("SharedResources", assemblyName.Name); - _htmlLocalizer = htmlFactory.Create("SharedResources", assemblyName.Name); + var assemblyName = typeof(SharedResources).Assembly.GetName()!; + _stringLocalizer = stringFactory.Create("SharedResources", assemblyName.Name!); + _htmlLocalizer = htmlFactory.Create("SharedResources", assemblyName.Name!); } public LocalizedHtmlString this[string name] => _htmlLocalizer[name]; diff --git a/src/Core/Services/Implementations/MailKitSmtpMailDeliveryService.cs b/src/Core/Services/Implementations/MailKitSmtpMailDeliveryService.cs index 3a7cabd39ed4..6e3fcb166bca 100644 --- a/src/Core/Services/Implementations/MailKitSmtpMailDeliveryService.cs +++ b/src/Core/Services/Implementations/MailKitSmtpMailDeliveryService.cs @@ -1,4 +1,4 @@ -using System.Security.Cryptography.X509Certificates; +using System.Security.Cryptography.X509Certificates; using Bit.Core.Platform.X509ChainCustomization; using Bit.Core.Settings; using Bit.Core.Utilities; @@ -22,12 +22,17 @@ public MailKitSmtpMailDeliveryService( ILogger logger, IOptions x509ChainOptions) { - if (globalSettings.Mail?.Smtp?.Host == null) + if (globalSettings.Mail.Smtp?.Host == null) { 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("@")) { diff --git a/src/Core/Services/NoopImplementations/NoopLicensingService.cs b/src/Core/Services/NoopImplementations/NoopLicensingService.cs index dc733e9a3387..b181e6113801 100644 --- a/src/Core/Services/NoopImplementations/NoopLicensingService.cs +++ b/src/Core/Services/NoopImplementations/NoopLicensingService.cs @@ -1,4 +1,6 @@ -using System.Security.Claims; +#nullable enable + +using System.Security.Claims; using Bit.Core.AdminConsole.Entities; using Bit.Core.Entities; using Bit.Core.Models.Business; @@ -45,28 +47,28 @@ public byte[] SignLicense(ILicense license) return new byte[0]; } - public Task ReadOrganizationLicenseAsync(Organization organization) + public Task ReadOrganizationLicenseAsync(Organization organization) { - return Task.FromResult(null); + return Task.FromResult(null); } - public Task ReadOrganizationLicenseAsync(Guid organizationId) + public Task ReadOrganizationLicenseAsync(Guid organizationId) { - return Task.FromResult(null); + return Task.FromResult(null); } - public ClaimsPrincipal GetClaimsPrincipalFromLicense(ILicense license) + public ClaimsPrincipal? GetClaimsPrincipalFromLicense(ILicense license) { return null; } - public Task CreateOrganizationTokenAsync(Organization organization, Guid installationId, SubscriptionInfo subscriptionInfo) + public Task CreateOrganizationTokenAsync(Organization organization, Guid installationId, SubscriptionInfo subscriptionInfo) { - return Task.FromResult(null); + return Task.FromResult(null); } - public Task CreateUserTokenAsync(User user, SubscriptionInfo subscriptionInfo) + public Task CreateUserTokenAsync(User user, SubscriptionInfo subscriptionInfo) { - return Task.FromResult(null); + return Task.FromResult(null); } } diff --git a/src/Core/Services/NoopImplementations/NoopMailService.cs b/src/Core/Services/NoopImplementations/NoopMailService.cs index d829fbbacbb1..aced8c4964b4 100644 --- a/src/Core/Services/NoopImplementations/NoopMailService.cs +++ b/src/Core/Services/NoopImplementations/NoopMailService.cs @@ -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.Auth.Entities; using Bit.Core.Billing.Enums; @@ -137,7 +139,7 @@ public Task SendAddedCreditAsync(string email, decimal amount) return Task.FromResult(0); } - public Task SendLicenseExpiredAsync(IEnumerable emails, string organizationName = null) + public Task SendLicenseExpiredAsync(IEnumerable emails, string? organizationName = null) { return Task.FromResult(0); } diff --git a/src/Core/Utilities/CoreHelpers.cs b/src/Core/Utilities/CoreHelpers.cs index d7fe51cfb636..415c18ce88c8 100644 --- a/src/Core/Utilities/CoreHelpers.cs +++ b/src/Core/Utilities/CoreHelpers.cs @@ -1,4 +1,7 @@ -using System.Globalization; +#nullable enable + +using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Reflection; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; @@ -116,11 +119,11 @@ public static string CleanCertificateThumbprint(string thumbprint) 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); - X509Certificate2 cert = null; + X509Certificate2? cert = null; var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser); certStore.Open(OpenFlags.ReadOnly); var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false); @@ -141,7 +144,7 @@ public static X509Certificate2 GetCertificate(string file, string password) public async static Task GetEmbeddedCertificateAsync(string file, string password) { 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()) { await s.CopyToAsync(ms); @@ -153,14 +156,14 @@ public static string GetEmbeddedResourceContentsAsync(string file) { var assembly = Assembly.GetCallingAssembly(); 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)) { return reader.ReadToEnd(); } } - public async static Task GetBlobCertificateAsync(string connectionString, string container, string file, string password) + public async static Task GetBlobCertificateAsync(string connectionString, string container, string file, string password) { try { @@ -233,7 +236,7 @@ public static string SecureRandomString(int length, string characters) 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."); } @@ -344,12 +347,12 @@ public static string ReadableBytesSize(long size) /// This method is subject to the limitations of System.Text.Json. For example, properties with /// inaccessible setters will not be set. /// - public static T CloneObject(T obj) + public static T? CloneObject(T obj) { return JsonSerializer.Deserialize(JsonSerializer.Serialize(obj)); } - public static bool SettingHasValue(string setting) + public static bool SettingHasValue([NotNullWhen(true)] string? setting) { var normalizedSetting = setting?.ToLowerInvariant(); return !string.IsNullOrWhiteSpace(normalizedSetting) && !normalizedSetting.Equals("secret") && @@ -448,7 +451,8 @@ public static string TransformFromBase64Url(string input) return output; } - public static string PunyEncode(string text) + [return: NotNullIfNotNull(nameof(text))] + public static string? PunyEncode(string? text) { if (text == "") { @@ -473,21 +477,21 @@ public static string PunyEncode(string text) } } - public static string FormatLicenseSignatureValue(object val) + public static string? FormatLicenseSignatureValue(object val) { if (val == null) { 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) @@ -625,7 +629,7 @@ public static string GetApplicationCacheServiceBusSubscriptionName(GlobalSetting return subName; } - public static string GetIpAddress(this Microsoft.AspNetCore.Http.HttpContext httpContext, + public static string? GetIpAddress(this Microsoft.AspNetCore.Http.HttpContext httpContext, GlobalSettings globalSettings) { if (httpContext == null) @@ -652,7 +656,7 @@ public static bool IsCorsOriginAllowed(string origin, GlobalSettings globalSetti (!globalSettings.SelfHosted && origin == "https://bitwarden.com"); } - public static X509Certificate2 GetIdentityServerCertificate(GlobalSettings globalSettings) + public static X509Certificate2? GetIdentityServerCertificate(GlobalSettings globalSettings) { if (globalSettings.SelfHosted && SettingHasValue(globalSettings.IdentityServer.CertificatePassword) @@ -811,7 +815,9 @@ public static List> BuildIdentityClaims(User user, return new T(); } +#nullable disable // TODO: Remove this and fix any callee warnings. return System.Text.Json.JsonSerializer.Deserialize(jsonData, _jsonSerializerOptions); +#nullable enable } public static string ClassToJsonData(T data) @@ -829,7 +835,7 @@ public static ICollection AddIfNotExists(this ICollection list, T item) return list; } - public static string DecodeMessageText(this QueueMessage message) + public static string? DecodeMessageText(this QueueMessage message) { var text = message?.MessageText; if (string.IsNullOrWhiteSpace(text)) @@ -852,7 +858,7 @@ public static bool FixedTimeEquals(string input1, string input2) Encoding.UTF8.GetBytes(input1), Encoding.UTF8.GetBytes(input2)); } - public static string ObfuscateEmail(string email) + public static string? ObfuscateEmail(string email) { if (email == null) { @@ -886,7 +892,7 @@ public static string ObfuscateEmail(string email) } - public static string GetEmailDomain(string email) + public static string? GetEmailDomain(string email) { if (!string.IsNullOrWhiteSpace(email)) { @@ -906,7 +912,7 @@ public static string ReplaceWhiteSpace(string input, string newValue) return _whiteSpaceRegex.Replace(input, newValue); } - public static string RedactEmailAddress(string email) + public static string? RedactEmailAddress(string email) { if (string.IsNullOrWhiteSpace(email)) { diff --git a/src/Infrastructure.EntityFramework/Repositories/CollectionRepository.cs b/src/Infrastructure.EntityFramework/Repositories/CollectionRepository.cs index 0bb6fb99250f..8f843b75d901 100644 --- a/src/Infrastructure.EntityFramework/Repositories/CollectionRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/CollectionRepository.cs @@ -513,8 +513,8 @@ public async Task> GetManyUsersByIdAsync( } } - public async Task ReplaceAsync(Core.Entities.Collection collection, IEnumerable groups, - IEnumerable users) + public async Task ReplaceAsync(Core.Entities.Collection collection, IEnumerable? groups, + IEnumerable? users) { await UpsertAsync(collection); using (var scope = ServiceScopeFactory.CreateScope()) From 5a49f20bd0da6dca1817fef199c525c3efc28ac9 Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Fri, 11 Apr 2025 14:45:21 -0400 Subject: [PATCH 2/3] Fix build errors --- src/Core/Services/IMailService.cs | 4 +++- .../Services/Implementations/HandlebarsMailService.cs | 6 +++--- .../Services/NoopImplementations/NoopMailService.cs | 2 +- src/Core/Utilities/CoreHelpers.cs | 6 +++--- .../Repositories/CollectionRepository.cs | 10 ++++++++-- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Core/Services/IMailService.cs b/src/Core/Services/IMailService.cs index 52724721ee68..fc5134586597 100644 --- a/src/Core/Services/IMailService.cs +++ b/src/Core/Services/IMailService.cs @@ -97,9 +97,11 @@ Task SendProviderUpdatePaymentMethod( Task SendInitiateDeletProviderEmailAsync(string email, Provider provider, string token); Task SendInitiateDeleteOrganzationEmailAsync(string email, Organization organization, string token); Task SendRequestSMAccessToAdminEmailAsync(IEnumerable adminEmails, string organizationName, string userRequestingAccess, string emailContent); +#nullable disable Task SendFamiliesForEnterpriseRemoveSponsorshipsEmailAsync(string email, string offerAcceptanceDate, string organizationId, string organizationName); +#nullable enable Task SendClaimedDomainUserEmailAsync(ManagedUserDomainClaimedEmails emailList); - Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable adminEmails, Guid organizationId, string email, string userName); + Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable adminEmails, Guid organizationId, string email, string? userName); Task SendBulkSecurityTaskNotificationsAsync(Organization org, IEnumerable securityTaskNotifications, IEnumerable adminOwnerEmails); } diff --git a/src/Core/Services/Implementations/HandlebarsMailService.cs b/src/Core/Services/Implementations/HandlebarsMailService.cs index ae569263b1d6..f3cc70566c9d 100644 --- a/src/Core/Services/Implementations/HandlebarsMailService.cs +++ b/src/Core/Services/Implementations/HandlebarsMailService.cs @@ -762,7 +762,7 @@ private async Task RegisterHelpersAndPartialsAsync() var emailList = new List(); 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 emails) { @@ -1267,7 +1267,7 @@ public async Task SendFamiliesForEnterpriseRemoveSponsorshipsEmailAsync(string e await _mailDeliveryService.SendEmailAsync(message); } - public async Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable adminEmails, Guid organizationId, string email, string userName) + public async Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable adminEmails, Guid organizationId, string email, string? userName) { var templateName = _globalSettings.SelfHosted ? "AdminConsole.SelfHostNotifyAdminDeviceApprovalRequested" : @@ -1304,7 +1304,7 @@ MailQueueMessage CreateMessage(UserSecurityTasksCount notification) 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); } diff --git a/src/Core/Services/NoopImplementations/NoopMailService.cs b/src/Core/Services/NoopImplementations/NoopMailService.cs index aced8c4964b4..697aa33a62aa 100644 --- a/src/Core/Services/NoopImplementations/NoopMailService.cs +++ b/src/Core/Services/NoopImplementations/NoopMailService.cs @@ -321,7 +321,7 @@ public Task SendFamiliesForEnterpriseRemoveSponsorshipsEmailAsync(string email, } public Task SendClaimedDomainUserEmailAsync(ManagedUserDomainClaimedEmails emailList) => Task.CompletedTask; - public Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable adminEmails, Guid organizationId, string email, string userName) + public Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable adminEmails, Guid organizationId, string email, string? userName) { return Task.FromResult(0); } diff --git a/src/Core/Utilities/CoreHelpers.cs b/src/Core/Utilities/CoreHelpers.cs index 415c18ce88c8..3ad28519b733 100644 --- a/src/Core/Utilities/CoreHelpers.cs +++ b/src/Core/Utilities/CoreHelpers.cs @@ -347,9 +347,9 @@ public static string ReadableBytesSize(long size) /// This method is subject to the limitations of System.Text.Json. For example, properties with /// inaccessible setters will not be set. /// - public static T? CloneObject(T obj) + public static T CloneObject(T obj) { - return JsonSerializer.Deserialize(JsonSerializer.Serialize(obj)); + return JsonSerializer.Deserialize(JsonSerializer.Serialize(obj))!; } public static bool SettingHasValue([NotNullWhen(true)] string? setting) @@ -808,7 +808,7 @@ public static List> BuildIdentityClaims(User user, /// The JSON data /// The type to deserialize into /// - public static T LoadClassFromJsonData(string jsonData) where T : new() + public static T LoadClassFromJsonData(string? jsonData) where T : new() { if (string.IsNullOrWhiteSpace(jsonData)) { diff --git a/src/Infrastructure.EntityFramework/Repositories/CollectionRepository.cs b/src/Infrastructure.EntityFramework/Repositories/CollectionRepository.cs index 8f843b75d901..73268d75bf4e 100644 --- a/src/Infrastructure.EntityFramework/Repositories/CollectionRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/CollectionRepository.cs @@ -520,8 +520,14 @@ public async Task ReplaceAsync(Core.Entities.Collection collection, IEnumerable< using (var scope = ServiceScopeFactory.CreateScope()) { var dbContext = GetDatabaseContext(scope); - await ReplaceCollectionGroupsAsync(dbContext, collection, groups); - await ReplaceCollectionUsersAsync(dbContext, collection, users); + if (groups != null) + { + await ReplaceCollectionGroupsAsync(dbContext, collection, groups); + } + if (users != null) + { + await ReplaceCollectionUsersAsync(dbContext, collection, users); + } await dbContext.UserBumpAccountRevisionDateByCollectionIdAsync(collection.Id, collection.OrganizationId); await dbContext.SaveChangesAsync(); } From 0eeebf764fed249e242bd9c0319f1b58578380a9 Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Mon, 14 Apr 2025 13:35:39 -0400 Subject: [PATCH 3/3] Format --- src/Core/Platform/Push/Services/IPushRegistrationService.cs | 2 +- src/Core/Platform/Push/Services/NoopPushRegistrationService.cs | 2 +- .../Services/Implementations/MailKitSmtpMailDeliveryService.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Core/Platform/Push/Services/IPushRegistrationService.cs b/src/Core/Platform/Push/Services/IPushRegistrationService.cs index 7dff9fd2f27a..8e34e5e31624 100644 --- a/src/Core/Platform/Push/Services/IPushRegistrationService.cs +++ b/src/Core/Platform/Push/Services/IPushRegistrationService.cs @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable using Bit.Core.Enums; using Bit.Core.NotificationHub; diff --git a/src/Core/Platform/Push/Services/NoopPushRegistrationService.cs b/src/Core/Platform/Push/Services/NoopPushRegistrationService.cs index 7eadb834e99c..32efc95ce605 100644 --- a/src/Core/Platform/Push/Services/NoopPushRegistrationService.cs +++ b/src/Core/Platform/Push/Services/NoopPushRegistrationService.cs @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable using Bit.Core.Enums; using Bit.Core.NotificationHub; diff --git a/src/Core/Services/Implementations/MailKitSmtpMailDeliveryService.cs b/src/Core/Services/Implementations/MailKitSmtpMailDeliveryService.cs index 6e3fcb166bca..2ebc7492f7c8 100644 --- a/src/Core/Services/Implementations/MailKitSmtpMailDeliveryService.cs +++ b/src/Core/Services/Implementations/MailKitSmtpMailDeliveryService.cs @@ -1,4 +1,4 @@ -using System.Security.Cryptography.X509Certificates; +using System.Security.Cryptography.X509Certificates; using Bit.Core.Platform.X509ChainCustomization; using Bit.Core.Settings; using Bit.Core.Utilities;