From 4571ecb0e319c12f1a39a52076aa7900f4e2c41f Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Thu, 31 Mar 2022 14:35:23 +0200 Subject: [PATCH] More work on nullable reference types --- .../Extensions/ClaimsIdentityExtensions.cs | 3 +- .../Models/ContentEditing/ContentItemBasic.cs | 2 +- .../ContentEditing/ContentItemDisplay.cs | 2 +- .../Models/ContentEditing/ContentItemSave.cs | 2 +- src/Umbraco.Core/Models/IconModel.cs | 4 +- ...erLoginRequiresVerificationNotification.cs | 2 +- .../PropertyEditors/IValueValidator.cs | 2 +- src/Umbraco.Core/Services/IIconService.cs | 4 +- .../Services/IPropertyValidationService.cs | 8 +- .../WebAssets/IRuntimeMinifier.cs | 2 +- .../Security/BackOfficeUserStore.cs | 2 +- .../Security/IUmbracoUserManager.cs | 8 +- .../Security/IUserSessionStore.cs | 2 +- .../Security/UmbracoUserManager.cs | 6 +- .../ContentPermissionsResource.cs | 4 +- .../AppendCurrentEventMessagesAttribute.cs | 2 +- .../AppendUserModifiedHeaderAttribute.cs | 12 +-- .../CheckIfUserTicketDataIsStaleAttribute.cs | 6 +- .../Filters/ContentModelValidator.cs | 76 ++++++++++--------- .../Filters/ContentSaveValidationAttribute.cs | 43 ++++++----- .../Filters/DataTypeValidateAttribute.cs | 41 ++++++---- .../FileUploadCleanupFilterAttribute.cs | 6 +- .../FilterAllowedOutgoingContentAttribute.cs | 8 +- .../FilterAllowedOutgoingMediaAttribute.cs | 20 ++--- .../IsCurrentUserModelFilterAttribute.cs | 8 +- .../MediaItemSaveValidationAttribute.cs | 14 ++-- .../Filters/MemberSaveModelValidator.cs | 50 +++++++----- .../Filters/MemberSaveValidationAttribute.cs | 4 +- .../MinifyJavaScriptResultAttribute.cs | 9 ++- .../OutgoingEditorModelEventAttribute.cs | 2 +- .../Filters/UserGroupValidateAttribute.cs | 13 ++-- .../HealthChecks/HealthCheckController.cs | 6 +- ...CreateUnattendedUserNotificationHandler.cs | 8 +- .../Install/InstallApiController.cs | 16 ++-- .../Install/InstallAreaRoutes.cs | 4 +- .../Install/InstallController.cs | 2 +- .../Mapping/CommonTreeNodeMapper.cs | 2 +- .../Mapping/ContentMapDefinition.cs | 51 +++++++------ .../Mapping/MediaMapDefinition.cs | 4 +- .../Mapping/MemberMapDefinition.cs | 5 +- .../ModelBinders/BlueprintItemBinder.cs | 2 +- .../ModelBinders/ContentItemBinder.cs | 4 +- .../ModelBinders/ContentModelBinderHelper.cs | 19 +++-- .../ModelBinders/FromJsonPathAttribute.cs | 4 +- .../ModelBinders/MediaItemBinder.cs | 4 +- .../ModelBinders/MemberBinder.cs | 4 +- .../ContentTypeModelValidatorBase.cs | 4 +- .../ModelsBuilder/DashboardReport.cs | 2 +- .../ModelsBuilderDashboardController.cs | 6 +- .../ModelsBuilderDashboardProvider.cs | 2 +- .../PropertyEditors/RteEmbedController.cs | 4 +- .../PropertyEditors/TagsDataController.cs | 4 +- .../ContentPropertyValidationResult.cs | 2 +- .../Validation/ValidationResultConverter.cs | 12 +-- .../Security/BackOfficeAntiforgery.cs | 4 +- .../BackOfficeAuthenticationBuilder.cs | 6 +- .../Security/BackOfficeCookieManager.cs | 6 +- .../BackOfficeExternaLoginProviderScheme.cs | 2 +- .../BackOfficeExternalLoginProvider.cs | 4 +- .../BackOfficeExternalLoginProviderOptions.cs | 6 +- .../BackOfficeExternalLoginProviders.cs | 10 +-- .../BackOfficeExternalLoginsBuilder.cs | 4 +- .../Security/BackOfficeSecureDataFormat.cs | 12 +-- .../Security/BackOfficeSessionIdValidator.cs | 4 +- .../Security/BackOfficeSignInManager.cs | 8 +- .../Security/BackOfficeUserManagerAuditer.cs | 8 +- .../ConfigureBackOfficeCookieOptions.cs | 16 ++-- .../Security/ExternalSignInAutoLinkOptions.cs | 10 +-- .../Security/IBackOfficeAntiforgery.cs | 2 +- .../IBackOfficeExternalLoginProviders.cs | 4 +- .../Security/IBackOfficeTwoFactorOptions.cs | 2 +- .../Security/IPasswordChanger.cs | 2 +- .../NoopBackOfficeTwoFactorOptions.cs | 2 +- .../Security/PasswordChanger.cs | 2 +- .../Services/IconService.cs | 18 ++--- .../Trees/ContentTreeController.cs | 4 +- .../Trees/ContentTreeControllerBase.cs | 4 +- .../Trees/ITreeNodeController.cs | 2 +- .../Trees/MediaTreeController.cs | 2 +- .../Trees/MemberTreeController.cs | 4 +- .../Trees/TreeControllerBase.cs | 6 +- .../Trees/UrlHelperExtensions.cs | 8 +- .../EndpointRouteBuilderExtensions.cs | 10 +-- .../Extensions/HttpContextExtensions.cs | 2 +- .../IModelsBuilderDashboardProvider.cs | 2 +- .../SmidgeRuntimeMinifier.cs | 2 +- .../Security/BackOfficeUserManager.cs | 8 +- .../Security/IBackOfficeSignInManager.cs | 4 +- 88 files changed, 388 insertions(+), 340 deletions(-) diff --git a/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs b/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs index 27331d31a5..e3d6f7f4fd 100644 --- a/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs +++ b/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Security.Claims; @@ -113,7 +114,7 @@ namespace Umbraco.Extensions /// /// Verified identity wrapped in a ClaimsIdentity with BackOfficeAuthentication type /// True if ClaimsIdentity - public static bool VerifyBackOfficeIdentity(this ClaimsIdentity identity, out ClaimsIdentity? verifiedIdentity) + public static bool VerifyBackOfficeIdentity(this ClaimsIdentity identity, [MaybeNullWhen(false)] out ClaimsIdentity verifiedIdentity) { if (identity is null) { diff --git a/src/Umbraco.Core/Models/ContentEditing/ContentItemBasic.cs b/src/Umbraco.Core/Models/ContentEditing/ContentItemBasic.cs index 2c9e498a85..9b1fcdc129 100644 --- a/src/Umbraco.Core/Models/ContentEditing/ContentItemBasic.cs +++ b/src/Umbraco.Core/Models/ContentEditing/ContentItemBasic.cs @@ -40,7 +40,7 @@ namespace Umbraco.Cms.Core.Models.ContentEditing [DataMember(Name = "contentTypeAlias", IsRequired = true)] [Required(AllowEmptyStrings = false)] - public string? ContentTypeAlias { get; set; } + public string ContentTypeAlias { get; set; } = null!; [DataMember(Name = "sortOrder")] public int SortOrder { get; set; } diff --git a/src/Umbraco.Core/Models/ContentEditing/ContentItemDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/ContentItemDisplay.cs index a795f85724..85968222e7 100644 --- a/src/Umbraco.Core/Models/ContentEditing/ContentItemDisplay.cs +++ b/src/Umbraco.Core/Models/ContentEditing/ContentItemDisplay.cs @@ -136,7 +136,7 @@ namespace Umbraco.Cms.Core.Models.ContentEditing public int TemplateId { get; set; } [DataMember(Name = "allowedTemplates")] - public IDictionary? AllowedTemplates { get; set; } + public IDictionary? AllowedTemplates { get; set; } [DataMember(Name = "documentType")] public ContentTypeBasic? DocumentType { get; set; } diff --git a/src/Umbraco.Core/Models/ContentEditing/ContentItemSave.cs b/src/Umbraco.Core/Models/ContentEditing/ContentItemSave.cs index 622276cecf..fb142048dd 100644 --- a/src/Umbraco.Core/Models/ContentEditing/ContentItemSave.cs +++ b/src/Umbraco.Core/Models/ContentEditing/ContentItemSave.cs @@ -30,7 +30,7 @@ namespace Umbraco.Cms.Core.Models.ContentEditing [DataMember(Name = "contentTypeAlias", IsRequired = true)] [Required(AllowEmptyStrings = false)] - public string? ContentTypeAlias { get; set; } + public string ContentTypeAlias { get; set; } = null!; /// /// The template alias to save diff --git a/src/Umbraco.Core/Models/IconModel.cs b/src/Umbraco.Core/Models/IconModel.cs index 081b0516ad..6b09c08602 100644 --- a/src/Umbraco.Core/Models/IconModel.cs +++ b/src/Umbraco.Core/Models/IconModel.cs @@ -2,7 +2,7 @@ namespace Umbraco.Cms.Core.Models { public class IconModel { - public string? Name { get; set; } - public string? SvgString { get; set; } + public string Name { get; set; } = null!; + public string SvgString { get; set; } = null!; } } diff --git a/src/Umbraco.Core/Notifications/UserLoginRequiresVerificationNotification.cs b/src/Umbraco.Core/Notifications/UserLoginRequiresVerificationNotification.cs index b99bf58b7e..5a975a1951 100644 --- a/src/Umbraco.Core/Notifications/UserLoginRequiresVerificationNotification.cs +++ b/src/Umbraco.Core/Notifications/UserLoginRequiresVerificationNotification.cs @@ -2,7 +2,7 @@ namespace Umbraco.Cms.Core.Notifications { public class UserLoginRequiresVerificationNotification : UserNotification { - public UserLoginRequiresVerificationNotification(string ipAddress, string affectedUserId, string performingUserId) : base(ipAddress, affectedUserId, performingUserId) + public UserLoginRequiresVerificationNotification(string ipAddress, string? affectedUserId, string performingUserId) : base(ipAddress, affectedUserId, performingUserId) { } } diff --git a/src/Umbraco.Core/PropertyEditors/IValueValidator.cs b/src/Umbraco.Core/PropertyEditors/IValueValidator.cs index 45f094de56..b4304fad59 100644 --- a/src/Umbraco.Core/PropertyEditors/IValueValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/IValueValidator.cs @@ -18,6 +18,6 @@ namespace Umbraco.Cms.Core.PropertyEditors /// /// The value can be a string, a Json structure (JObject, JArray...)... corresponding to what was posted by an editor. /// - IEnumerable Validate(object? value, string valueType, object? dataTypeConfiguration); + IEnumerable Validate(object? value, string? valueType, object? dataTypeConfiguration); } } diff --git a/src/Umbraco.Core/Services/IIconService.cs b/src/Umbraco.Core/Services/IIconService.cs index aa848bb1e9..0b215c481c 100644 --- a/src/Umbraco.Core/Services/IIconService.cs +++ b/src/Umbraco.Core/Services/IIconService.cs @@ -12,12 +12,12 @@ namespace Umbraco.Cms.Core.Services /// /// /// - IconModel GetIcon(string iconName); + IconModel? GetIcon(string iconName); /// /// Gets a list of all svg icons found at at the global icons path. /// /// - IReadOnlyDictionary GetIcons(); + IReadOnlyDictionary? GetIcons(); } } diff --git a/src/Umbraco.Core/Services/IPropertyValidationService.cs b/src/Umbraco.Core/Services/IPropertyValidationService.cs index 8c7aee93df..c2b8824340 100644 --- a/src/Umbraco.Core/Services/IPropertyValidationService.cs +++ b/src/Umbraco.Core/Services/IPropertyValidationService.cs @@ -23,11 +23,11 @@ namespace Umbraco.Cms.Core.Services IEnumerable ValidatePropertyValue( IDataEditor editor, IDataType dataType, - object postedValue, + object? postedValue, bool isRequired, - string validationRegExp, - string isRequiredMessage, - string validationRegExpMessage); + string? validationRegExp, + string? isRequiredMessage, + string? validationRegExpMessage); /// /// Validates a property value. diff --git a/src/Umbraco.Core/WebAssets/IRuntimeMinifier.cs b/src/Umbraco.Core/WebAssets/IRuntimeMinifier.cs index 0c8067def6..baf9549562 100644 --- a/src/Umbraco.Core/WebAssets/IRuntimeMinifier.cs +++ b/src/Umbraco.Core/WebAssets/IRuntimeMinifier.cs @@ -83,7 +83,7 @@ namespace Umbraco.Cms.Core.WebAssets /// /// /// - Task MinifyAsync(string fileContent, AssetType assetType); + Task MinifyAsync(string? fileContent, AssetType assetType); /// /// Ensures that all runtime minifications are refreshed on next request. E.g. Clearing cache. diff --git a/src/Umbraco.Infrastructure/Security/BackOfficeUserStore.cs b/src/Umbraco.Infrastructure/Security/BackOfficeUserStore.cs index e8613a7c5e..a9c324ebca 100644 --- a/src/Umbraco.Infrastructure/Security/BackOfficeUserStore.cs +++ b/src/Umbraco.Infrastructure/Security/BackOfficeUserStore.cs @@ -582,7 +582,7 @@ namespace Umbraco.Cms.Core.Security } /// - public Task ValidateSessionIdAsync(string userId, string sessionId) + public Task ValidateSessionIdAsync(string? userId, string? sessionId) { if (Guid.TryParse(sessionId, out Guid guidSessionId)) { diff --git a/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs b/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs index 5c46308cb1..851560b0ba 100644 --- a/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs +++ b/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs @@ -77,7 +77,7 @@ namespace Umbraco.Cms.Core.Security /// We use this because in the back office the only way an admin can change another user's password without first knowing their password /// is to generate a token and reset it, however, when we do this we want to track a password change, not a password reset /// - Task ChangePasswordWithResetAsync(string userId, string token, string newPassword); + Task ChangePasswordWithResetAsync(string userId, string token, string? newPassword); /// /// Validates that an email confirmation token matches the specified . @@ -186,7 +186,7 @@ namespace Umbraco.Cms.Core.Security /// The that represents the asynchronous operation, containing true if /// the specified matches the one store for the , /// otherwise false. - Task CheckPasswordAsync(TUser user, string password); + Task CheckPasswordAsync(TUser user, string? password); /// /// Changes a user's password after confirming the specified is correct, @@ -199,13 +199,13 @@ namespace Umbraco.Cms.Core.Security /// The that represents the asynchronous operation, containing the /// of the operation. /// - Task ChangePasswordAsync(TUser user, string currentPassword, string newPassword); + Task ChangePasswordAsync(TUser user, string? currentPassword, string? newPassword); /// /// Used to validate a user's session /// /// Returns true if the session is valid, otherwise false - Task ValidateSessionIdAsync(string userId, string sessionId); + Task ValidateSessionIdAsync(string? userId, string? sessionId); /// /// Creates the specified in the backing store with no password, diff --git a/src/Umbraco.Infrastructure/Security/IUserSessionStore.cs b/src/Umbraco.Infrastructure/Security/IUserSessionStore.cs index 9bcda2c2c4..56f5ecabeb 100644 --- a/src/Umbraco.Infrastructure/Security/IUserSessionStore.cs +++ b/src/Umbraco.Infrastructure/Security/IUserSessionStore.cs @@ -12,6 +12,6 @@ namespace Umbraco.Cms.Core.Security /// /// Validates a user's session is still valid /// - Task ValidateSessionIdAsync(string userId, string sessionId); + Task ValidateSessionIdAsync(string? userId, string? sessionId); } } diff --git a/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs b/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs index 3a75511fb7..74538f14c0 100644 --- a/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs +++ b/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs @@ -73,7 +73,7 @@ namespace Umbraco.Cms.Core.Security /// The user id /// The session id /// True if the session is valid, else false - public virtual async Task ValidateSessionIdAsync(string userId, string sessionId) + public virtual async Task ValidateSessionIdAsync(string? userId, string? sessionId) { // if this is not set, for backwards compat (which would be super rare), we'll just approve it // TODO: This should be removed after members supports this @@ -132,7 +132,7 @@ namespace Umbraco.Cms.Core.Security } /// - public override async Task CheckPasswordAsync(TUser user, string password) + public override async Task CheckPasswordAsync(TUser user, string? password) { // we cannot proceed if the user passed in does not have an identity if (user.HasIdentity == false) @@ -155,7 +155,7 @@ namespace Umbraco.Cms.Core.Security /// We use this because in the back office the only way an admin can change another user's password without first knowing their password /// is to generate a token and reset it, however, when we do this we want to track a password change, not a password reset /// - public virtual async Task ChangePasswordWithResetAsync(string userId, string token, string newPassword) + public virtual async Task ChangePasswordWithResetAsync(string userId, string token, string? newPassword) { TUser user = await FindByIdAsync(userId); if (user == null) diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResource.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResource.cs index 4c9da3ae41..ae02db6d4f 100644 --- a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResource.cs +++ b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResource.cs @@ -39,7 +39,7 @@ namespace Umbraco.Cms.Web.BackOffice.Authorization /// The content. /// The node Id. /// The collection of permissions to authorize. - public ContentPermissionsResource(IContent content, int nodeId, IReadOnlyList permissionsToCheck) + public ContentPermissionsResource(IContent? content, int nodeId, IReadOnlyList permissionsToCheck) { Content = content; NodeId = nodeId; @@ -59,6 +59,6 @@ namespace Umbraco.Cms.Web.BackOffice.Authorization /// /// Gets the content. /// - public IContent Content { get; } + public IContent? Content { get; } } } diff --git a/src/Umbraco.Web.BackOffice/Filters/AppendCurrentEventMessagesAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/AppendCurrentEventMessagesAttribute.cs index 8df4192906..67dab8dcb7 100644 --- a/src/Umbraco.Web.BackOffice/Filters/AppendCurrentEventMessagesAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/AppendCurrentEventMessagesAttribute.cs @@ -72,7 +72,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters throw new ArgumentOutOfRangeException(); } - notifications.Notifications.Add(new BackOfficeNotification + notifications.Notifications?.Add(new BackOfficeNotification { Message = eventMessage.Message, Header = eventMessage.Category, diff --git a/src/Umbraco.Web.BackOffice/Filters/AppendUserModifiedHeaderAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/AppendUserModifiedHeaderAttribute.cs index 842e455199..ebc5d59a75 100644 --- a/src/Umbraco.Web.BackOffice/Filters/AppendUserModifiedHeaderAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/AppendUserModifiedHeaderAttribute.cs @@ -11,7 +11,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters /// public sealed class AppendUserModifiedHeaderAttribute : ActionFilterAttribute { - private readonly string _userIdParameter; + private readonly string? _userIdParameter; /// /// An empty constructor which will always set the header. @@ -38,19 +38,19 @@ namespace Umbraco.Cms.Web.BackOffice.Filters } else { - if (!context.ActionArguments.ContainsKey(_userIdParameter)) + if (!context.ActionArguments.ContainsKey(_userIdParameter!)) { throw new InvalidOperationException($"No argument found for the current action with the name: {_userIdParameter}"); } var backofficeSecurityAccessor = context.HttpContext.RequestServices.GetService(); - var user = backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser; + var user = backofficeSecurityAccessor?.BackOfficeSecurity?.CurrentUser; if (user == null) { return; } - var userId = GetUserIdFromParameter(context.ActionArguments[_userIdParameter]); + var userId = GetUserIdFromParameter(context.ActionArguments[_userIdParameter!]); if (userId == user.Id) { AppendHeader(context); @@ -67,14 +67,14 @@ namespace Umbraco.Cms.Web.BackOffice.Filters } } - private int GetUserIdFromParameter(object parameterValue) + private int GetUserIdFromParameter(object? parameterValue) { if (parameterValue is int) { return (int)parameterValue; } - throw new InvalidOperationException($"The id type: {parameterValue.GetType()} is not a supported id."); + throw new InvalidOperationException($"The id type: {parameterValue?.GetType()} is not a supported id."); } } } diff --git a/src/Umbraco.Web.BackOffice/Filters/CheckIfUserTicketDataIsStaleAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/CheckIfUserTicketDataIsStaleAttribute.cs index 5b3b877bb9..0c065c000f 100644 --- a/src/Umbraco.Web.BackOffice/Filters/CheckIfUserTicketDataIsStaleAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/CheckIfUserTicketDataIsStaleAttribute.cs @@ -91,7 +91,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters var tokenFilter = new SetAngularAntiForgeryTokensAttribute.SetAngularAntiForgeryTokensFilter(_backOfficeAntiforgery); await tokenFilter.OnActionExecutionAsync( actionContext, - () => Task.FromResult(new ActionExecutedContext(actionContext, new List(), null))); + () => Task.FromResult(new ActionExecutedContext(actionContext, new List(), new { }))); // add the header AppendUserModifiedHeaderAttribute.AppendHeader(actionContext); @@ -124,7 +124,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters return; } - IUser user = _userService.GetUserById(id.Value); + IUser? user = _userService.GetUserById(id.Value); if (user == null) { return; @@ -165,7 +165,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters /// private async Task ReSync(IUser user, ActionExecutingContext actionContext) { - BackOfficeIdentityUser backOfficeIdentityUser = _umbracoMapper.Map(user); + BackOfficeIdentityUser? backOfficeIdentityUser = _umbracoMapper.Map(user); await _backOfficeSignInManager.SignInAsync(backOfficeIdentityUser, isPersistent: true); // flag that we've made changes diff --git a/src/Umbraco.Web.BackOffice/Filters/ContentModelValidator.cs b/src/Umbraco.Web.BackOffice/Filters/ContentModelValidator.cs index 5aea9bb950..e3377f8557 100644 --- a/src/Umbraco.Web.BackOffice/Filters/ContentModelValidator.cs +++ b/src/Umbraco.Web.BackOffice/Filters/ContentModelValidator.cs @@ -58,9 +58,9 @@ namespace Umbraco.Cms.Web.BackOffice.Filters /// /// /// - public virtual bool ValidateExistingContent(TModelSave postedItem, ActionExecutingContext actionContext) + public virtual bool ValidateExistingContent(TModelSave? postedItem, ActionExecutingContext actionContext) { - var persistedContent = postedItem.PersistedContent; + var persistedContent = postedItem?.PersistedContent; if (persistedContent == null) { actionContext.Result = new NotFoundObjectResult("content was not found"); @@ -77,10 +77,10 @@ namespace Umbraco.Cms.Web.BackOffice.Filters /// /// /// - public virtual bool ValidateProperties(TModelSave model, IContentProperties modelWithProperties, ActionExecutingContext actionContext) + public virtual bool ValidateProperties(TModelSave? model, IContentProperties? modelWithProperties, ActionExecutingContext actionContext) { - var persistedContent = model.PersistedContent; - return ValidateProperties(modelWithProperties.Properties.ToList(), persistedContent.Properties.ToList(), actionContext); + var persistedContent = model?.PersistedContent; + return ValidateProperties(modelWithProperties?.Properties.ToList() ?? new List(), persistedContent?.Properties.ToList(), actionContext); } /// @@ -90,11 +90,16 @@ namespace Umbraco.Cms.Web.BackOffice.Filters /// /// /// - protected bool ValidateProperties(List postedProperties, List persistedProperties, ActionExecutingContext actionContext) + protected bool ValidateProperties(List? postedProperties, List? persistedProperties, ActionExecutingContext actionContext) { + if (postedProperties is null) + { + return false; + } + foreach (var p in postedProperties) { - if (persistedProperties.Any(property => property.Alias == p.Alias) == false) + if (persistedProperties?.Any(property => property.Alias == p.Alias) == false) { // TODO: Do we return errors here ? If someone deletes a property whilst their editing then should we just //save the property data that remains? Or inform them they need to reload... not sure. This problem exists currently too i think. @@ -120,36 +125,39 @@ namespace Umbraco.Cms.Web.BackOffice.Filters /// All property data validation goes into the model state with a prefix of "Properties" /// public virtual bool ValidatePropertiesData( - TModelSave model, - TModelWithProperties modelWithProperties, - ContentPropertyCollectionDto dto, + TModelSave? model, + TModelWithProperties? modelWithProperties, + ContentPropertyCollectionDto? dto, ModelStateDictionary modelState) { - var properties = modelWithProperties.Properties.ToDictionary(x => x.Alias, x => x); + var properties = modelWithProperties?.Properties.ToDictionary(x => x.Alias, x => x); - foreach (var p in dto.Properties) + if (dto is not null) { - var editor = p.PropertyEditor; - - if (editor == null) + foreach (var p in dto.Properties) { - var message = $"Could not find property editor \"{p.DataType.EditorAlias}\" for property with id {p.Id}."; + var editor = p.PropertyEditor; - Logger.LogWarning(message); - continue; + if (editor == null) + { + var message = $"Could not find property editor \"{p.DataType?.EditorAlias}\" for property with id {p.Id}."; + + Logger.LogWarning(message); + continue; + } + + //get the posted value for this property, this may be null in cases where the property was marked as readonly which means + //the angular app will not post that value. + if (properties is null || !properties.TryGetValue(p.Alias, out var postedProp)) + continue; + + var postedValue = postedProp.Value; + + ValidatePropertyValue(model, modelWithProperties, editor, p, postedValue, modelState); } - - //get the posted value for this property, this may be null in cases where the property was marked as readonly which means - //the angular app will not post that value. - if (!properties.TryGetValue(p.Alias, out var postedProp)) - continue; - - var postedValue = postedProp.Value; - - ValidatePropertyValue(model, modelWithProperties, editor, p, postedValue, modelState); - } + return modelState.IsValid; } @@ -165,11 +173,11 @@ namespace Umbraco.Cms.Web.BackOffice.Filters /// /// protected virtual void ValidatePropertyValue( - TModelSave model, - TModelWithProperties modelWithProperties, + TModelSave? model, + TModelWithProperties? modelWithProperties, IDataEditor editor, ContentPropertyDto property, - object postedValue, + object? postedValue, ModelStateDictionary modelState) { if (property is null) throw new ArgumentNullException(nameof(property)); @@ -184,14 +192,14 @@ namespace Umbraco.Cms.Web.BackOffice.Filters } protected virtual void AddPropertyError( - TModelSave model, - TModelWithProperties modelWithProperties, + TModelSave? model, + TModelWithProperties? modelWithProperties, IDataEditor editor, ContentPropertyDto property, ValidationResult validationResult, ModelStateDictionary modelState) { - modelState.AddPropertyError(validationResult, property.Alias, property.Culture, property.Segment); + modelState.AddPropertyError(validationResult, property.Alias, property.Culture ?? string.Empty, property.Segment ?? string.Empty); } } diff --git a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs index 4b8fb6acb3..430d38022a 100644 --- a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs @@ -65,7 +65,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters private async Task OnActionExecutingAsync(ActionExecutingContext context) { - var model = (ContentItemSave) context.ActionArguments["contentItem"]; + var model = (ContentItemSave?) context.ActionArguments["contentItem"]; var contentItemValidator = new ContentSaveModelValidator(_loggerFactory.CreateLogger(), _propertyValidationService); if (context.ModelState.ContainsKey("contentItem")) @@ -78,12 +78,17 @@ namespace Umbraco.Cms.Web.BackOffice.Filters if (!contentItemValidator.ValidateExistingContent(model, context)) return; if (!await ValidateUserAccessAsync(model, context)) return; - //validate for each variant that is being updated - foreach (var variant in model.Variants.Where(x => x.Save)) + if (model is not null) { - if (contentItemValidator.ValidateProperties(model, variant, context)) - contentItemValidator.ValidatePropertiesData(model, variant, variant.PropertyCollectionDto, - context.ModelState); + //validate for each variant that is being updated + foreach (var variant in model.Variants.Where(x => x.Save)) + { + if (contentItemValidator.ValidateProperties(model, variant, context)) + { + contentItemValidator.ValidatePropertiesData(model, variant, variant.PropertyCollectionDto, + context.ModelState); + } + } } } @@ -95,10 +100,10 @@ namespace Umbraco.Cms.Web.BackOffice.Filters /// /// private bool ValidateAtLeastOneVariantIsBeingSaved( - ContentItemSave contentItem, + ContentItemSave? contentItem, ActionExecutingContext actionContext) { - if (!contentItem.Variants.Any(x => x.Save)) + if (!contentItem?.Variants.Any(x => x.Save) ?? true) { actionContext.Result = new NotFoundObjectResult(new {Message = "No variants flagged for saving"}); return false; @@ -114,7 +119,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters /// /// private async Task ValidateUserAccessAsync( - ContentItemSave contentItem, + ContentItemSave? contentItem, ActionExecutingContext actionContext) { // We now need to validate that the user is allowed to be doing what they are doing. @@ -122,32 +127,32 @@ namespace Umbraco.Cms.Web.BackOffice.Filters // Then if it is new, we need to lookup those permissions on the parent! var permissionToCheck = new List(); - IContent contentToCheck = null; + IContent? contentToCheck = null; int contentIdToCheck; - switch (contentItem.Action) + switch (contentItem?.Action) { case ContentSaveAction.Save: permissionToCheck.Add(ActionUpdate.ActionLetter); contentToCheck = contentItem.PersistedContent; - contentIdToCheck = contentToCheck.Id; + contentIdToCheck = contentToCheck?.Id ?? default; break; case ContentSaveAction.Publish: case ContentSaveAction.PublishWithDescendants: case ContentSaveAction.PublishWithDescendantsForce: permissionToCheck.Add(ActionPublish.ActionLetter); contentToCheck = contentItem.PersistedContent; - contentIdToCheck = contentToCheck.Id; + contentIdToCheck = contentToCheck?.Id ?? default; break; case ContentSaveAction.SendPublish: permissionToCheck.Add(ActionToPublish.ActionLetter); contentToCheck = contentItem.PersistedContent; - contentIdToCheck = contentToCheck.Id; + contentIdToCheck = contentToCheck?.Id ?? default; break; case ContentSaveAction.Schedule: permissionToCheck.Add(ActionUpdate.ActionLetter); permissionToCheck.Add(ActionToPublish.ActionLetter); contentToCheck = contentItem.PersistedContent; - contentIdToCheck = contentToCheck.Id; + contentIdToCheck = contentToCheck?.Id ?? default; break; case ContentSaveAction.SaveNew: //Save new requires ActionNew @@ -157,7 +162,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters if (contentItem.ParentId != Constants.System.Root) { contentToCheck = _contentService.GetById(contentItem.ParentId); - contentIdToCheck = contentToCheck.Id; + contentIdToCheck = contentToCheck?.Id ?? default; } else { @@ -173,7 +178,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters if (contentItem.ParentId != Constants.System.Root) { contentToCheck = _contentService.GetById(contentItem.ParentId); - contentIdToCheck = contentToCheck.Id; + contentIdToCheck = contentToCheck?.Id ?? default; } else { @@ -193,7 +198,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters if (contentItem.ParentId != Constants.System.Root) { contentToCheck = _contentService.GetById(contentItem.ParentId); - contentIdToCheck = contentToCheck.Id; + contentIdToCheck = contentToCheck?.Id ?? default; } else { @@ -210,7 +215,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters if (contentItem.ParentId != Constants.System.Root) { contentToCheck = _contentService.GetById(contentItem.ParentId); - contentIdToCheck = contentToCheck.Id; + contentIdToCheck = contentToCheck?.Id ?? default; } else { diff --git a/src/Umbraco.Web.BackOffice/Filters/DataTypeValidateAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/DataTypeValidateAttribute.cs index b912c982a1..9d9f0324c1 100644 --- a/src/Umbraco.Web.BackOffice/Filters/DataTypeValidateAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/DataTypeValidateAttribute.cs @@ -42,24 +42,31 @@ namespace Umbraco.Cms.Web.BackOffice.Filters public void OnActionExecuting(ActionExecutingContext context) { - var dataType = (DataTypeSave)context.ActionArguments["dataType"]; - - dataType.Name = dataType.Name.CleanForXss('[', ']', '(', ')', ':'); - dataType.Alias = dataType.Alias == null ? dataType.Name : dataType.Alias.CleanForXss('[', ']', '(', ')', ':'); + var dataType = (DataTypeSave?)context.ActionArguments["dataType"]; + if (dataType is not null) + { + dataType.Name = dataType.Name?.CleanForXss('[', ']', '(', ')', ':'); + dataType.Alias = dataType.Alias == null ? dataType.Name! : dataType.Alias.CleanForXss('[', ']', '(', ')', ':'); + } // get the property editor, ensuring that it exits - if (!_propertyEditorCollection.TryGet(dataType.EditorAlias, out var propertyEditor)) + if (!_propertyEditorCollection.TryGet(dataType?.EditorAlias, out var propertyEditor)) { - var message = $"Property editor \"{dataType.EditorAlias}\" was not found."; + var message = $"Property editor \"{dataType?.EditorAlias}\" was not found."; context.Result = new UmbracoProblemResult(message, HttpStatusCode.NotFound); return; } + if (dataType is null) + { + return; + } + // assign dataType.PropertyEditor = propertyEditor; // validate that the data type exists, or create one if required - IDataType persisted; + IDataType? persisted; switch (dataType.Action) { case ContentSaveAction.Save: @@ -77,7 +84,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters case ContentSaveAction.SaveNew: // create the persisted model from mapping the saved model persisted = _umbracoMapper.Map(dataType); - ((DataType)persisted).ResetIdentity(); + ((DataType?)persisted)?.ResetIdentity(); break; default: @@ -91,15 +98,19 @@ namespace Umbraco.Cms.Web.BackOffice.Filters // validate the configuration // which is posted as a set of fields with key (string) and value (object) var configurationEditor = propertyEditor.GetConfigurationEditor(); - foreach (var field in dataType.ConfigurationFields) - { - var editorField = configurationEditor.Fields.SingleOrDefault(x => x.Key == field.Key); - if (editorField == null) continue; - // run each IValueValidator (with null valueType and dataTypeConfiguration: not relevant here) - foreach (var validator in editorField.Validators) + if (dataType.ConfigurationFields is not null) + { + foreach (var field in dataType.ConfigurationFields) + { + var editorField = configurationEditor.Fields.SingleOrDefault(x => x.Key == field.Key); + if (editorField == null) continue; + + // run each IValueValidator (with null valueType and dataTypeConfiguration: not relevant here) + foreach (var validator in editorField.Validators) foreach (var result in validator.Validate(field.Value, null, null)) - context.ModelState.AddValidationError(result, "Properties", field.Key); + context.ModelState.AddValidationError(result, "Properties", field.Key ?? string.Empty); + } } if (context.ModelState.IsValid == false) diff --git a/src/Umbraco.Web.BackOffice/Filters/FileUploadCleanupFilterAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/FileUploadCleanupFilterAttribute.cs index 6586e1a250..22770abced 100644 --- a/src/Umbraco.Web.BackOffice/Filters/FileUploadCleanupFilterAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/FileUploadCleanupFilterAttribute.cs @@ -61,7 +61,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters { //track all temp folders so we can remove old files afterwards var dir = Path.GetDirectoryName(f.TempFilePath); - if (tempFolders.Contains(dir) == false) + if (dir is not null && tempFolders.Contains(dir) == false) { tempFolders.Add(dir); } @@ -109,7 +109,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters { //track all temp folders so we can remove old files afterwards var dir = Path.GetDirectoryName(f.TempFilePath); - if (tempFolders.Contains(dir) == false) + if (dir is not null && tempFolders.Contains(dir) == false) { tempFolders.Add(dir); } @@ -143,7 +143,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters { _logger.LogWarning( "The actionExecutedContext.Request.Content.Value is not IHaveUploadedFiles, it is {ObjectType}", - objectResult.Value.GetType()); + objectResult.Value?.GetType()); } } } diff --git a/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingContentAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingContentAttribute.cs index 5056357781..011e8275ab 100644 --- a/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingContentAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingContentAttribute.cs @@ -43,12 +43,12 @@ namespace Umbraco.Cms.Web.BackOffice.Filters } - public FilterAllowedOutgoingContentAttribute(Type outgoingType, string propertyName, char permissionToCheck) + public FilterAllowedOutgoingContentAttribute(Type outgoingType, string? propertyName, char permissionToCheck) : base(typeof(FilterAllowedOutgoingContentFilter)) { Arguments = new object[] { - outgoingType, propertyName, permissionToCheck + outgoingType, propertyName ?? string.Empty, permissionToCheck }; } } @@ -75,7 +75,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters FilterBasedOnPermissions(items, user); } - protected override int[] GetUserStartNodes(IUser user) + protected override int[]? GetUserStartNodes(IUser user) { return user.CalculateContentStartNodeIds(_entityService, _appCaches); } @@ -94,7 +94,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters var ids = new List(); for (var i = 0; i < length; i++) { - ids.Add(((dynamic)items[i]).Id); + ids.Add(((dynamic)items[i]!).Id); } //get all the permissions for these nodes in one call var permissions = _userService.GetPermissions(user, ids.ToArray()); diff --git a/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingMediaAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingMediaAttribute.cs index d9f44e733c..528c2a1c22 100644 --- a/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingMediaAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingMediaAttribute.cs @@ -22,12 +22,12 @@ namespace Umbraco.Cms.Web.BackOffice.Filters /// internal class FilterAllowedOutgoingMediaAttribute : TypeFilterAttribute { - public FilterAllowedOutgoingMediaAttribute(Type outgoingType, string propertyName = null) + public FilterAllowedOutgoingMediaAttribute(Type outgoingType, string? propertyName = null) : base(typeof(FilterAllowedOutgoingMediaFilter)) { Arguments = new object[] { - outgoingType, propertyName + outgoingType, propertyName ?? string.Empty }; } } @@ -54,7 +54,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters _outgoingType = outgoingType; } - protected virtual int[] GetUserStartNodes(IUser user) + protected virtual int[]? GetUserStartNodes(IUser user) { return user.CalculateMediaStartNodeIds(_entityService, _appCaches); } @@ -65,7 +65,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters { if (context.Result == null) return; - var user = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser; + var user = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; if (user == null) return; var objectContent = context.Result as ObjectResult; @@ -95,7 +95,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters var toRemove = new List(); foreach (dynamic item in items) { - var hasPathAccess = (item != null && ContentPermissions.HasPathAccess(item.Path, GetUserStartNodes(user), RecycleBinId)); + var hasPathAccess = (item != null && ContentPermissions.HasPathAccess(item?.Path, GetUserStartNodes(user), RecycleBinId)); if (hasPathAccess == false) { toRemove.Add(item); @@ -110,14 +110,14 @@ namespace Umbraco.Cms.Web.BackOffice.Filters private void SetValueForResponse(ObjectResult objectContent, dynamic newVal) { - if (TypeHelper.IsTypeAssignableFrom(_outgoingType, objectContent.Value.GetType())) + if (TypeHelper.IsTypeAssignableFrom(_outgoingType, objectContent.Value?.GetType())) { objectContent.Value = newVal; } else if (_propertyName.IsNullOrWhiteSpace() == false) { //try to get the enumerable collection from a property on the result object using reflection - var property = objectContent.Value.GetType().GetProperty(_propertyName); + var property = objectContent.Value?.GetType().GetProperty(_propertyName); if (property != null) { property.SetValue(objectContent.Value, newVal); @@ -126,9 +126,9 @@ namespace Umbraco.Cms.Web.BackOffice.Filters } - internal dynamic GetValueFromResponse(ObjectResult objectContent) + internal dynamic? GetValueFromResponse(ObjectResult objectContent) { - if (TypeHelper.IsTypeAssignableFrom(_outgoingType, objectContent.Value.GetType())) + if (TypeHelper.IsTypeAssignableFrom(_outgoingType, objectContent.Value?.GetType())) { return objectContent.Value; } @@ -136,7 +136,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters if (_propertyName.IsNullOrWhiteSpace() == false) { //try to get the enumerable collection from a property on the result object using reflection - var property = objectContent.Value.GetType().GetProperty(_propertyName); + var property = objectContent.Value?.GetType().GetProperty(_propertyName); if (property != null) { var result = property.GetValue(objectContent.Value); diff --git a/src/Umbraco.Web.BackOffice/Filters/IsCurrentUserModelFilterAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/IsCurrentUserModelFilterAttribute.cs index 31750f1e66..bf61b3cfa4 100644 --- a/src/Umbraco.Web.BackOffice/Filters/IsCurrentUserModelFilterAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/IsCurrentUserModelFilterAttribute.cs @@ -27,7 +27,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters { if (context.Result == null) return; - var user = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser; + var user = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; if (user == null) return; var objectContent = context.Result as ObjectResult; @@ -36,7 +36,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters var model = objectContent.Value as UserBasic; if (model != null) { - model.IsCurrentUser = (int) model.Id == user.Id; + model.IsCurrentUser = (int?) model.Id == user.Id; } else { @@ -45,7 +45,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters { foreach (var userBasic in collection) { - userBasic.IsCurrentUser = (int) userBasic.Id == user.Id; + userBasic.IsCurrentUser = (int?) userBasic.Id == user.Id; } } else @@ -55,7 +55,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters { foreach (var userBasic in paged.Items) { - userBasic.IsCurrentUser = (int)userBasic.Id == user.Id; + userBasic.IsCurrentUser = (int?)userBasic.Id == user.Id; } } } diff --git a/src/Umbraco.Web.BackOffice/Filters/MediaItemSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/MediaItemSaveValidationAttribute.cs index ff5f8a83c1..ef558a9f17 100644 --- a/src/Umbraco.Web.BackOffice/Filters/MediaItemSaveValidationAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/MediaItemSaveValidationAttribute.cs @@ -57,7 +57,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters private async Task OnActionExecutingAsync(ActionExecutingContext context) { - var model = (MediaItemSave) context.ActionArguments["contentItem"]; + var model = (MediaItemSave?) context.ActionArguments["contentItem"]; var contentItemValidator = new MediaSaveModelValidator(_loggerFactory.CreateLogger(), _propertyValidationService); if (await ValidateUserAccessAsync(model, context)) @@ -65,7 +65,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters //now do each validation step if (contentItemValidator.ValidateExistingContent(model, context)) if (contentItemValidator.ValidateProperties(model, model, context)) - contentItemValidator.ValidatePropertiesData(model, model, model.PropertyCollectionDto, + contentItemValidator.ValidatePropertiesData(model, model, model?.PropertyCollectionDto, context.ModelState); } } @@ -75,17 +75,17 @@ namespace Umbraco.Cms.Web.BackOffice.Filters /// /// /// - private async Task ValidateUserAccessAsync(MediaItemSave mediaItem, ActionExecutingContext actionContext) + private async Task ValidateUserAccessAsync(MediaItemSave? mediaItem, ActionExecutingContext actionContext) { //We now need to validate that the user is allowed to be doing what they are doing. //Then if it is new, we need to lookup those permissions on the parent. - IMedia contentToCheck; + IMedia? contentToCheck; int contentIdToCheck; - switch (mediaItem.Action) + switch (mediaItem?.Action) { case ContentSaveAction.Save: contentToCheck = mediaItem.PersistedContent; - contentIdToCheck = contentToCheck.Id; + contentIdToCheck = contentToCheck?.Id ?? default; break; case ContentSaveAction.SaveNew: contentToCheck = _mediaService.GetById(mediaItem.ParentId); @@ -93,7 +93,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters if (mediaItem.ParentId != Constants.System.Root) { contentToCheck = _mediaService.GetById(mediaItem.ParentId); - contentIdToCheck = contentToCheck.Id; + contentIdToCheck = contentToCheck?.Id ?? default; } else { diff --git a/src/Umbraco.Web.BackOffice/Filters/MemberSaveModelValidator.cs b/src/Umbraco.Web.BackOffice/Filters/MemberSaveModelValidator.cs index 57fc834e0c..6eb6bd6620 100644 --- a/src/Umbraco.Web.BackOffice/Filters/MemberSaveModelValidator.cs +++ b/src/Umbraco.Web.BackOffice/Filters/MemberSaveModelValidator.cs @@ -22,14 +22,14 @@ namespace Umbraco.Cms.Web.BackOffice.Filters /// internal class MemberSaveModelValidator : ContentModelValidator> { - private readonly IBackOfficeSecurity _backofficeSecurity; + private readonly IBackOfficeSecurity? _backofficeSecurity; private readonly IMemberTypeService _memberTypeService; private readonly IMemberService _memberService; private readonly IShortStringHelper _shortStringHelper; public MemberSaveModelValidator( ILogger logger, - IBackOfficeSecurity backofficeSecurity, + IBackOfficeSecurity? backofficeSecurity, IMemberTypeService memberTypeService, IMemberService memberService, IShortStringHelper shortStringHelper, @@ -42,9 +42,14 @@ namespace Umbraco.Cms.Web.BackOffice.Filters _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); } - public override bool ValidatePropertiesData(MemberSave model, IContentProperties modelWithProperties, ContentPropertyCollectionDto dto, + public override bool ValidatePropertiesData(MemberSave? model, IContentProperties? modelWithProperties, ContentPropertyCollectionDto? dto, ModelStateDictionary modelState) { + if (model is null) + { + return false; + } + if (model.Username.IsNullOrWhiteSpace()) { modelState.AddPropertyError( @@ -87,42 +92,45 @@ namespace Umbraco.Cms.Web.BackOffice.Filters /// /// /// - public override bool ValidateProperties(MemberSave model, IContentProperties modelWithProperties, ActionExecutingContext actionContext) + public override bool ValidateProperties(MemberSave? model, IContentProperties? modelWithProperties, ActionExecutingContext actionContext) { - var propertiesToValidate = model.Properties.ToList(); + var propertiesToValidate = model?.Properties.ToList(); var defaultProps = ConventionsHelper.GetStandardPropertyTypeStubs(_shortStringHelper); var exclude = defaultProps.Select(x => x.Value.Alias).ToArray(); foreach (var remove in exclude) { - propertiesToValidate.RemoveAll(property => property.Alias == remove); + propertiesToValidate?.RemoveAll(property => property.Alias == remove); } //if the user doesn't have access to sensitive values, then we need to validate the incoming properties to check //if a sensitive value is being submitted. - if (_backofficeSecurity.CurrentUser.HasAccessToSensitiveData() == false) + if (_backofficeSecurity?.CurrentUser?.HasAccessToSensitiveData() == false) { - var contentType = _memberTypeService.Get(model.PersistedContent.ContentTypeId); - var sensitiveProperties = contentType + var contentType = _memberTypeService.Get(model?.PersistedContent?.ContentTypeId ?? default); + var sensitiveProperties = contentType? .PropertyTypes.Where(x => contentType.IsSensitiveProperty(x.Alias)) .ToList(); - foreach (var sensitiveProperty in sensitiveProperties) + if (sensitiveProperties is not null) { - var prop = propertiesToValidate.FirstOrDefault(x => x.Alias == sensitiveProperty.Alias); - - if (prop != null) + foreach (var sensitiveProperty in sensitiveProperties) { - //this should not happen, this means that there was data posted for a sensitive property that - //the user doesn't have access to, which means that someone is trying to hack the values. + var prop = propertiesToValidate?.FirstOrDefault(x => x.Alias == sensitiveProperty.Alias); - var message = $"property with alias: {prop.Alias} cannot be posted"; - actionContext.Result = new NotFoundObjectResult(new InvalidOperationException(message)); - return false; + if (prop != null) + { + //this should not happen, this means that there was data posted for a sensitive property that + //the user doesn't have access to, which means that someone is trying to hack the values. + + var message = $"property with alias: {prop.Alias} cannot be posted"; + actionContext.Result = new NotFoundObjectResult(new InvalidOperationException(message)); + return false; + } } } } - return ValidateProperties(propertiesToValidate, model.PersistedContent.Properties.ToList(), actionContext); + return ValidateProperties(propertiesToValidate, model?.PersistedContent?.Properties.ToList(), actionContext); } internal bool ValidateUniqueLogin(MemberSave model) @@ -135,7 +143,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters case ContentSaveAction.Save: //ok, we're updating the member, we need to check if they are changing their login and if so, does it exist already ? - if (model.PersistedContent.Username.InvariantEquals(model.Username.Trim()) == false) + if (model.PersistedContent?.Username.InvariantEquals(model.Username.Trim()) == false) { //they are changing their login name if (existingByName != null && existingByName.Username == model.Username.Trim()) @@ -170,7 +178,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters { case ContentSaveAction.Save: //ok, we're updating the member, we need to check if they are changing their email and if so, does it exist already ? - if (model.PersistedContent.Email.InvariantEquals(model.Email.Trim()) == false) + if (model.PersistedContent?.Email.InvariantEquals(model.Email.Trim()) == false) { //they are changing their email if (existingByEmail != null && existingByEmail.Email.InvariantEquals(model.Email.Trim())) diff --git a/src/Umbraco.Web.BackOffice/Filters/MemberSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/MemberSaveValidationAttribute.cs index e01f9197e1..3175f703f0 100644 --- a/src/Umbraco.Web.BackOffice/Filters/MemberSaveValidationAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/MemberSaveValidationAttribute.cs @@ -46,12 +46,12 @@ namespace Umbraco.Cms.Web.BackOffice.Filters public void OnActionExecuting(ActionExecutingContext context) { - var model = (MemberSave)context.ActionArguments["contentItem"]; + var model = (MemberSave?)context.ActionArguments["contentItem"]; var contentItemValidator = new MemberSaveModelValidator(_loggerFactory.CreateLogger(), _backofficeSecurityAccessor.BackOfficeSecurity, _memberTypeService, _memberService, _shortStringHelper, _propertyValidationService); //now do each validation step if (contentItemValidator.ValidateExistingContent(model, context)) if (contentItemValidator.ValidateProperties(model, model, context)) - contentItemValidator.ValidatePropertiesData(model, model, model.PropertyCollectionDto, context.ModelState); + contentItemValidator.ValidatePropertiesData(model, model, model?.PropertyCollectionDto, context.ModelState); } public void OnActionExecuted(ActionExecutedContext context) diff --git a/src/Umbraco.Web.BackOffice/Filters/MinifyJavaScriptResultAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/MinifyJavaScriptResultAttribute.cs index 325fdfcc7f..4a9b7aaa72 100644 --- a/src/Umbraco.Web.BackOffice/Filters/MinifyJavaScriptResultAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/MinifyJavaScriptResultAttribute.cs @@ -14,15 +14,18 @@ namespace Umbraco.Cms.Web.BackOffice.Filters // logic before action goes here var serviceProvider = context.HttpContext.RequestServices; var hostingEnvironment = serviceProvider.GetService(); - if (!hostingEnvironment.IsDebugMode) + if (!hostingEnvironment?.IsDebugMode ?? false) { var runtimeMinifier = serviceProvider.GetService(); if (context.Result is JavaScriptResult jsResult) { var result = jsResult.Content; - var minified = await runtimeMinifier.MinifyAsync(result, AssetType.Javascript); - jsResult.Content = minified; + if (runtimeMinifier is not null) + { + var minified = await runtimeMinifier.MinifyAsync(result, AssetType.Javascript); + jsResult.Content = minified; + } } } diff --git a/src/Umbraco.Web.BackOffice/Filters/OutgoingEditorModelEventAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/OutgoingEditorModelEventAttribute.cs index 8899499887..3c9cde13fd 100644 --- a/src/Umbraco.Web.BackOffice/Filters/OutgoingEditorModelEventAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/OutgoingEditorModelEventAttribute.cs @@ -49,7 +49,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters if (context.Result == null) return; var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); - var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser; + var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; if (currentUser == null) return; if (context.Result is ObjectResult objectContent) diff --git a/src/Umbraco.Web.BackOffice/Filters/UserGroupValidateAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/UserGroupValidateAttribute.cs index 7edf790fb3..cea09b6c0a 100644 --- a/src/Umbraco.Web.BackOffice/Filters/UserGroupValidateAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/UserGroupValidateAttribute.cs @@ -34,14 +34,17 @@ namespace Umbraco.Cms.Web.BackOffice.Filters public void OnActionExecuting(ActionExecutingContext context) { - var userGroupSave = (UserGroupSave) context.ActionArguments["userGroupSave"]; + var userGroupSave = (UserGroupSave?) context.ActionArguments["userGroupSave"]; - userGroupSave.Name = userGroupSave.Name.CleanForXss('[', ']', '(', ')', ':'); - userGroupSave.Alias = userGroupSave.Alias.CleanForXss('[', ']', '(', ')', ':'); + if (userGroupSave is not null) + { + userGroupSave.Name = userGroupSave.Name?.CleanForXss('[', ']', '(', ')', ':'); + userGroupSave.Alias = userGroupSave.Alias.CleanForXss('[', ']', '(', ')', ':'); + } //Validate the usergroup exists or create one if required - IUserGroup persisted; - switch (userGroupSave.Action) + IUserGroup? persisted; + switch (userGroupSave?.Action) { case ContentSaveAction.Save: persisted = _userService.GetUserGroupById(Convert.ToInt32(userGroupSave.Id)); diff --git a/src/Umbraco.Web.BackOffice/HealthChecks/HealthCheckController.cs b/src/Umbraco.Web.BackOffice/HealthChecks/HealthCheckController.cs index eae1c7d2ce..cf2264ab5c 100644 --- a/src/Umbraco.Web.BackOffice/HealthChecks/HealthCheckController.cs +++ b/src/Umbraco.Web.BackOffice/HealthChecks/HealthCheckController.cs @@ -49,12 +49,12 @@ namespace Umbraco.Cms.Web.BackOffice.HealthChecks /// Returns a collection of anonymous objects representing each group. public object GetAllHealthChecks() { - IOrderedEnumerable> groups = _checks + IOrderedEnumerable> groups = _checks .Where(x => _disabledCheckIds.Contains(x.Id) == false) .GroupBy(x => x.Group) .OrderBy(x => x.Key); var healthCheckGroups = new List(); - foreach (IGrouping healthCheckGroup in groups) + foreach (IGrouping healthCheckGroup in groups) { var hcGroup = new HealthCheckGroup { @@ -101,7 +101,7 @@ namespace Umbraco.Cms.Web.BackOffice.HealthChecks private HealthCheck GetCheckById(Guid? id) { - HealthCheck check = _checks + HealthCheck? check = _checks .Where(x => _disabledCheckIds.Contains(x.Id) == false) .FirstOrDefault(x => x.Id == id); diff --git a/src/Umbraco.Web.BackOffice/Install/CreateUnattendedUserNotificationHandler.cs b/src/Umbraco.Web.BackOffice/Install/CreateUnattendedUserNotificationHandler.cs index 6eaed2f42d..b5f691162e 100644 --- a/src/Umbraco.Web.BackOffice/Install/CreateUnattendedUserNotificationHandler.cs +++ b/src/Umbraco.Web.BackOffice/Install/CreateUnattendedUserNotificationHandler.cs @@ -54,7 +54,7 @@ namespace Umbraco.Cms.Web.BackOffice.Install return; } - IUser admin = _userService.GetUserById(Core.Constants.Security.SuperUserId); + IUser? admin = _userService.GetUserById(Core.Constants.Security.SuperUserId); if (admin == null) { throw new InvalidOperationException("Could not find the super user!"); @@ -67,8 +67,8 @@ namespace Umbraco.Cms.Web.BackOffice.Install } // Update name, email & login & save user - admin.Name = unattendedName.Trim(); - admin.Email = unattendedEmail.Trim(); + admin.Name = unattendedName!.Trim(); + admin.Email = unattendedEmail!.Trim(); admin.Username = unattendedEmail.Trim(); _userService.Save(admin); @@ -89,7 +89,7 @@ namespace Umbraco.Cms.Web.BackOffice.Install throw new InvalidOperationException("Could not reset password: unable to generate internal reset token"); } - IdentityResult resetResult = await backOfficeUserManager.ChangePasswordWithResetAsync(membershipUser.Id, resetToken, unattendedPassword.Trim()); + IdentityResult resetResult = await backOfficeUserManager.ChangePasswordWithResetAsync(membershipUser.Id, resetToken, unattendedPassword!.Trim()); if (!resetResult.Succeeded) { throw new InvalidOperationException("Could not reset password: " + string.Join(", ", resetResult.Errors.ToErrorMessage())); diff --git a/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs b/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs index 29c64174dd..c0c69e8358 100644 --- a/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs +++ b/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs @@ -183,10 +183,11 @@ namespace Umbraco.Cms.Web.BackOffice.Install return new InstallProgressResultModel(true, "", ""); } - private static object GetInstruction(InstallInstructions installModel, InstallTrackingItem item, + private static object? GetInstruction(InstallInstructions installModel, InstallTrackingItem item, InstallSetupStep step) { - installModel.Instructions.TryGetValue(item.Name, out var instruction); // else null + object? instruction = null; + installModel.Instructions?.TryGetValue(item.Name, out instruction); // else null if (instruction is JObject jObject) { @@ -242,7 +243,7 @@ namespace Umbraco.Cms.Web.BackOffice.Install } // determines whether the step requires execution - internal bool StepRequiresExecution(InstallSetupStep step, object instruction) + internal bool StepRequiresExecution(InstallSetupStep step, object? instruction) { if (step == null) throw new ArgumentNullException(nameof(step)); @@ -260,7 +261,8 @@ namespace Umbraco.Cms.Web.BackOffice.Install try { var method = typedStepType.GetMethods().Single(x => x.Name == "RequiresExecution"); - return (bool) method.Invoke(step, new[] { model }); + var result = (bool?) method.Invoke(step, new[] { model }); + return result ?? false; } catch (Exception ex) { @@ -271,7 +273,7 @@ namespace Umbraco.Cms.Web.BackOffice.Install } // executes the step - internal async Task ExecuteStepAsync(InstallSetupStep step, object instruction) + internal async Task ExecuteStepAsync(InstallSetupStep step, object? instruction) { using (_proflog.TraceDuration($"Executing installation step: '{step.Name}'.", "Step completed")) @@ -290,8 +292,8 @@ namespace Umbraco.Cms.Web.BackOffice.Install try { var method = typedStepType.GetMethods().Single(x => x.Name == "ExecuteAsync"); - var task = (Task) method.Invoke(step, new[] { model }); - return await task; + var task = (Task?) method.Invoke(step, new[] { model }); + return await task!; } catch (Exception ex) { diff --git a/src/Umbraco.Web.BackOffice/Install/InstallAreaRoutes.cs b/src/Umbraco.Web.BackOffice/Install/InstallAreaRoutes.cs index 6280631c83..5d0239303a 100644 --- a/src/Umbraco.Web.BackOffice/Install/InstallAreaRoutes.cs +++ b/src/Umbraco.Web.BackOffice/Install/InstallAreaRoutes.cs @@ -30,7 +30,7 @@ namespace Umbraco.Cms.Web.BackOffice.Install switch (_runtime.Level) { case var _ when _runtime.EnableInstaller(): - + endpoints.MapUmbracoRoute(installPathSegment, Cms.Core.Constants.Web.Mvc.InstallArea, "api", includeControllerNameInRoute: false); endpoints.MapUmbracoRoute(installPathSegment, Cms.Core.Constants.Web.Mvc.InstallArea, string.Empty, includeControllerNameInRoute: false); @@ -48,7 +48,7 @@ namespace Umbraco.Cms.Web.BackOffice.Install endpoints.MapGet($"{installPathSegment}/{{controller?}}/{{action?}}", context => { // redirect to umbraco - context.Response.Redirect(_linkGenerator.GetBackOfficeUrl(_hostingEnvironment), false); + context.Response.Redirect(_linkGenerator.GetBackOfficeUrl(_hostingEnvironment)!, false); return Task.CompletedTask; }); diff --git a/src/Umbraco.Web.BackOffice/Install/InstallController.cs b/src/Umbraco.Web.BackOffice/Install/InstallController.cs index 17aba2b27e..c0cebfb9d7 100644 --- a/src/Umbraco.Web.BackOffice/Install/InstallController.cs +++ b/src/Umbraco.Web.BackOffice/Install/InstallController.cs @@ -67,7 +67,7 @@ namespace Umbraco.Cms.Web.BackOffice.Install var umbracoPath = Url.GetBackOfficeUrl(); if (_runtime.Level == RuntimeLevel.Run) - return Redirect(umbracoPath); + return Redirect(umbracoPath!); // TODO: Update for package migrations if (_runtime.Level == RuntimeLevel.Upgrade) diff --git a/src/Umbraco.Web.BackOffice/Mapping/CommonTreeNodeMapper.cs b/src/Umbraco.Web.BackOffice/Mapping/CommonTreeNodeMapper.cs index 9559275ec9..efee8ba3e6 100644 --- a/src/Umbraco.Web.BackOffice/Mapping/CommonTreeNodeMapper.cs +++ b/src/Umbraco.Web.BackOffice/Mapping/CommonTreeNodeMapper.cs @@ -17,7 +17,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping } - public string GetTreeNodeUrl(IContentBase source) + public string? GetTreeNodeUrl(IContentBase source) where TController : UmbracoApiController, ITreeNodeController { return _linkGenerator.GetUmbracoApiService(controller => controller.GetTreeNode(source.Key.ToString("N"), null)); diff --git a/src/Umbraco.Web.BackOffice/Mapping/ContentMapDefinition.cs b/src/Umbraco.Web.BackOffice/Mapping/ContentMapDefinition.cs index e647309bd9..6d954ad5af 100644 --- a/src/Umbraco.Web.BackOffice/Mapping/ContentMapDefinition.cs +++ b/src/Umbraco.Web.BackOffice/Mapping/ContentMapDefinition.cs @@ -112,7 +112,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping // Umbraco.Code.MapAll private static void Map(IContent source, ContentPropertyCollectionDto target, MapperContext context) { - target.Properties = context.MapEnumerable(source.Properties); + target.Properties = context.MapEnumerable(source.Properties).WhereNotNull(); } // Umbraco.Code.MapAll -AllowPreview -Errors -PersistedContent @@ -120,7 +120,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping { // Both GetActions and DetermineIsChildOfListView use parent, so get it once here // Parent might already be in context, so check there before using content service - IContent parent; + IContent? parent; if (context.Items.TryGetValue("Parent", out var parentObj) && parentObj is IContent typedParent) { @@ -161,7 +161,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping target.Variants = _contentVariantMapper.Map(source, context); target.ContentDto = new ContentPropertyCollectionDto(); - target.ContentDto.Properties = context.MapEnumerable(source.Properties); + target.ContentDto.Properties = context.MapEnumerable(source.Properties).WhereNotNull(); } // Umbraco.Code.MapAll -Segment -Language -DisplayName @@ -196,7 +196,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping target.Owner = _commonMapper.GetOwner(source, context); target.ParentId = source.ParentId; target.Path = source.Path; - target.Properties = context.MapEnumerable(source.Properties); + target.Properties = context.MapEnumerable(source.Properties).WhereNotNull(); target.SortOrder = source.SortOrder; target.State = _basicStateMapper.Map(source, context); target.Trashed = source.Trashed; @@ -206,7 +206,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping target.VariesByCulture = source.ContentType.VariesByCulture(); } - private IEnumerable GetActions(IContent source, IContent parent, MapperContext context) + private IEnumerable GetActions(IContent source, IContent? parent, MapperContext context) { var backOfficeSecurity = _backOfficeSecurityAccessor.BackOfficeSecurity; @@ -230,7 +230,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping { // If we already have permissions for a given path, // and the current user is the same as was used to generate the permissions, return the stored permissions. - if (backOfficeSecurity.CurrentUser.Id == currentUser.Id && + if (backOfficeSecurity.CurrentUser?.Id == currentUser.Id && permissionsDict.TryGetValue(path, out var permissions)) { return permissions.GetAllPermissions(); @@ -284,7 +284,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping return date ?? source.UpdateDate; } - private string GetName(IContent source, MapperContext context) + private string? GetName(IContent source, MapperContext context) { // invariant = only 1 name if (!source.ContentType.VariesByCulture()) @@ -299,7 +299,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping // if we don't have a name for a culture, it means the culture is not available, and // hey we should probably not be mapping it, but it's too late, return a fallback name - return source.CultureInfos.TryGetValue(culture, out var name) && !name.Name.IsNullOrWhiteSpace() ? name.Name : $"({source.Name})"; + return source.CultureInfos is not null && source.CultureInfos.TryGetValue(culture, out var name) && !name.Name.IsNullOrWhiteSpace() ? name.Name : $"({source.Name})"; } /// @@ -318,7 +318,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping /// false because the item is technically not being rendered as part of a list view but instead as a /// real tree node. If we didn't perform this check then tree syncing wouldn't work correctly. /// - private bool DetermineIsChildOfListView(IContent source, IContent parent, MapperContext context) + private bool DetermineIsChildOfListView(IContent source, IContent? parent, MapperContext context) { var userStartNodes = Array.Empty(); @@ -328,7 +328,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping if (context.HasItems && context.Items.TryGetValue("CurrentUser", out var usr) && usr is IUser currentUser) { userStartNodes = currentUser.CalculateContentStartNodeIds(_entityService, _appCaches); - if (!userStartNodes.Contains(Constants.System.Root)) + if (!userStartNodes?.Contains(Constants.System.Root) ?? false) { // return false if this is the user's actual start node, the node will be rendered in the tree // regardless of if it's a list view or not @@ -342,17 +342,20 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping var pathParts = parent.Path.Split(Constants.CharArrays.Comma).Select(x => int.TryParse(x, NumberStyles.Integer, CultureInfo.InvariantCulture, out var i) ? i : 0).ToList(); - // reduce the path parts so we exclude top level content items that - // are higher up than a user's start nodes - foreach (var n in userStartNodes) + if (userStartNodes is not null) { - var index = pathParts.IndexOf(n); - if (index != -1) + // reduce the path parts so we exclude top level content items that + // are higher up than a user's start nodes + foreach (var n in userStartNodes) { - // now trim all top level start nodes to the found index - for (var i = 0; i < index; i++) + var index = pathParts.IndexOf(n); + if (index != -1) { - pathParts.RemoveAt(0); + // now trim all top level start nodes to the found index + for (var i = 0; i < index; i++) + { + pathParts.RemoveAt(0); + } } } } @@ -364,7 +367,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping private DateTime? GetScheduledDate(IContent source, ContentScheduleAction action, MapperContext context) { _ = context.Items.TryGetValue("Schedule", out var untypedSchedule); - + if (untypedSchedule is not ContentScheduleCollection scheduleCollection) { throw new ApplicationException("GetScheduledDate requires a ContentScheduleCollection in the MapperContext for Key: Schedule"); @@ -375,22 +378,22 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping return schedule.FirstOrDefault()?.Date; // take the first, it's ordered by date } - private IDictionary GetAllowedTemplates(IContent source) + private IDictionary? GetAllowedTemplates(IContent source) { // Element types can't have templates, so no need to query to get the content type if (source.ContentType.IsElement) { - return new Dictionary(); + return new Dictionary(); } var contentType = _contentTypeService.Get(source.ContentTypeId); - return contentType.AllowedTemplates + return contentType?.AllowedTemplates? .Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false) .ToDictionary(t => t.Alias, t => _localizedTextService.UmbracoDictionaryTranslate(_cultureDictionary, t.Name)); } - private string GetDefaultTemplate(IContent source) + private string? GetDefaultTemplate(IContent source) { if (source == null) return null; @@ -406,7 +409,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping } var template = _fileService.GetTemplate(source.TemplateId.Value); - return template.Alias; + return template?.Alias; } } } diff --git a/src/Umbraco.Web.BackOffice/Mapping/MediaMapDefinition.cs b/src/Umbraco.Web.BackOffice/Mapping/MediaMapDefinition.cs index 9bff86e90b..1e4cd0fcbd 100644 --- a/src/Umbraco.Web.BackOffice/Mapping/MediaMapDefinition.cs +++ b/src/Umbraco.Web.BackOffice/Mapping/MediaMapDefinition.cs @@ -51,7 +51,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping // Umbraco.Code.MapAll private static void Map(IMedia source, ContentPropertyCollectionDto target, MapperContext context) { - target.Properties = context.MapEnumerable(source.Properties); + target.Properties = context.MapEnumerable(source.Properties).WhereNotNull(); } // Umbraco.Code.MapAll -Properties -Errors -Edited -Updater -Alias -IsContainer @@ -95,7 +95,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping target.Owner = _commonMapper.GetOwner(source, context); target.ParentId = source.ParentId; target.Path = source.Path; - target.Properties = context.MapEnumerable(source.Properties); + target.Properties = context.MapEnumerable(source.Properties).WhereNotNull(); target.SortOrder = source.SortOrder; target.State = null; target.Trashed = source.Trashed; diff --git a/src/Umbraco.Web.BackOffice/Mapping/MemberMapDefinition.cs b/src/Umbraco.Web.BackOffice/Mapping/MemberMapDefinition.cs index 8da173ce68..73b324fbb3 100644 --- a/src/Umbraco.Web.BackOffice/Mapping/MemberMapDefinition.cs +++ b/src/Umbraco.Web.BackOffice/Mapping/MemberMapDefinition.cs @@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Models.Mapping; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Web.BackOffice.Trees; +using Umbraco.Extensions; using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Web.BackOffice.Mapping @@ -81,7 +82,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping target.Owner = _commonMapper.GetOwner(source, context); target.ParentId = source.ParentId; target.Path = source.Path; - target.Properties = context.MapEnumerable(source.Properties); + target.Properties = context.MapEnumerable(source.Properties).WhereNotNull(); target.SortOrder = source.SortOrder; target.State = null; target.Udi = Udi.Create(Constants.UdiEntityType.Member, source.Key); @@ -113,7 +114,7 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping // Umbraco.Code.MapAll private static void Map(IMember source, ContentPropertyCollectionDto target, MapperContext context) { - target.Properties = context.MapEnumerable(source.Properties); + target.Properties = context.MapEnumerable(source.Properties).WhereNotNull(); } } } diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs b/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs index 124d4acc62..355953d278 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs @@ -16,7 +16,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelBinders _contentService = contentService; } - protected override IContent GetExisting(ContentItemSave model) + protected override IContent? GetExisting(ContentItemSave model) { return _contentService.GetBlueprintById(model.Id); } diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs b/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs index 81451399b9..3cbaf4a592 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs @@ -40,7 +40,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelBinders _modelBinderHelper = new ContentModelBinderHelper(); } - protected virtual IContent GetExisting(ContentItemSave model) + protected virtual IContent? GetExisting(ContentItemSave model) { return _contentService.GetById(model.Id); } @@ -74,7 +74,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelBinders } var persistedContent = ContentControllerBase.IsCreatingAction(model.Action) ? CreateNew(model) : GetExisting(model); - BindModel(model, persistedContent, _modelBinderHelper, _umbracoMapper); + BindModel(model, persistedContent!, _modelBinderHelper, _umbracoMapper); bindingContext.Result = ModelBindingResult.Success(model); } diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/ContentModelBinderHelper.cs b/src/Umbraco.Web.BackOffice/ModelBinders/ContentModelBinderHelper.cs index 1e6e6eb1ba..9193b5db96 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/ContentModelBinderHelper.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/ContentModelBinderHelper.cs @@ -16,7 +16,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelBinders /// internal class ContentModelBinderHelper { - public async Task BindModelFromMultipartRequestAsync( + public async Task BindModelFromMultipartRequestAsync( IJsonSerializer jsonSerializer, IHostingEnvironment hostingEnvironment, ModelBindingContext bindingContext) @@ -63,7 +63,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelBinders var propAlias = parts[1]; //if there are 3 parts part 3 is always culture - string culture = null; + string? culture = null; if (parts.Length > 2) { culture = parts[2]; @@ -75,7 +75,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelBinders } //if there are 4 parts part 4 is always segment - string segment = null; + string? segment = null; if (parts.Length > 3) { segment = parts[3]; @@ -118,16 +118,19 @@ namespace Umbraco.Cms.Web.BackOffice.ModelBinders /// /// public void MapPropertyValuesFromSaved(IContentProperties saveModel, - ContentPropertyCollectionDto dto) + ContentPropertyCollectionDto? dto) { //NOTE: Don't convert this to linq, this is much quicker foreach (var p in saveModel.Properties) { - foreach (var propertyDto in dto.Properties) + if (dto is not null) { - if (propertyDto.Alias != p.Alias) continue; - propertyDto.Value = p.Value; - break; + foreach (var propertyDto in dto.Properties) + { + if (propertyDto.Alias != p.Alias) continue; + propertyDto.Value = p.Value; + break; + } } } } diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/FromJsonPathAttribute.cs b/src/Umbraco.Web.BackOffice/ModelBinders/FromJsonPathAttribute.cs index f53fbd0b43..17ac03ca80 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/FromJsonPathAttribute.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/FromJsonPathAttribute.cs @@ -53,7 +53,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelBinders var json = JsonConvert.DeserializeObject(strJson); //if no explicit json path then use the model name - var match = json.SelectToken(bindingContext.FieldName ?? bindingContext.ModelName); + var match = json?.SelectToken(bindingContext.FieldName ?? bindingContext.ModelName); if (match == null) { @@ -79,7 +79,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelBinders return false; } - JToken match = json.SelectToken(bindingContext.FieldName); + JToken? match = json.SelectToken(bindingContext.FieldName); // ReSharper disable once InvertIf if (match != null) diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/MediaItemBinder.cs b/src/Umbraco.Web.BackOffice/ModelBinders/MediaItemBinder.cs index dc785034d6..c71c686827 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/MediaItemBinder.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/MediaItemBinder.cs @@ -59,12 +59,12 @@ namespace Umbraco.Cms.Web.BackOffice.ModelBinders _modelBinderHelper.MapPropertyValuesFromSaved(model, model.PropertyCollectionDto); } - model.Name = model.Name.Trim(); + model.Name = model.Name?.Trim(); bindingContext.Result = ModelBindingResult.Success(model); } - private IMedia GetExisting(MediaItemSave model) + private IMedia? GetExisting(MediaItemSave model) { return _mediaService.GetById(Convert.ToInt32(model.Id)); } diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/MemberBinder.cs b/src/Umbraco.Web.BackOffice/ModelBinders/MemberBinder.cs index ae3ac6e801..6f0c5bd2ec 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/MemberBinder.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/MemberBinder.cs @@ -69,7 +69,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelBinders _modelBinderHelper.MapPropertyValuesFromSaved(model, model.PropertyCollectionDto); } - model.Name = model.Name.Trim(); + model.Name = model.Name?.Trim(); bindingContext.Result = ModelBindingResult.Success(model); } @@ -115,7 +115,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelBinders FilterMembershipProviderProperties(contentType); //return the new member with the details filled in - return new Member(model.Name, model.Email, model.Username, model.Password.NewPassword, contentType); + return new Member(model.Name, model.Email, model.Username, model.Password?.NewPassword, contentType); } /// diff --git a/src/Umbraco.Web.BackOffice/ModelsBuilder/ContentTypeModelValidatorBase.cs b/src/Umbraco.Web.BackOffice/ModelsBuilder/ContentTypeModelValidatorBase.cs index 40ccd1f60b..0f70cb1326 100644 --- a/src/Umbraco.Web.BackOffice/ModelsBuilder/ContentTypeModelValidatorBase.cs +++ b/src/Umbraco.Web.BackOffice/ModelsBuilder/ContentTypeModelValidatorBase.cs @@ -59,7 +59,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelsBuilder var groupIndex = model.Groups.IndexOf(propertyGroup); var propertyIndex = propertyGroup.Properties.IndexOf(prop); - ValidationResult validationResult = ValidateProperty(prop, groupIndex, propertyIndex); + ValidationResult? validationResult = ValidateProperty(prop, groupIndex, propertyIndex); if (validationResult != null) { yield return validationResult; @@ -67,7 +67,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelsBuilder } } - private ValidationResult ValidateProperty(PropertyTypeBasic property, int groupIndex, int propertyIndex) + private ValidationResult? ValidateProperty(PropertyTypeBasic property, int groupIndex, int propertyIndex) { // don't let them match any properties or methods in IPublishedContent // TODO: There are probably more! diff --git a/src/Umbraco.Web.BackOffice/ModelsBuilder/DashboardReport.cs b/src/Umbraco.Web.BackOffice/ModelsBuilder/DashboardReport.cs index cb0eacbcae..e52846051d 100644 --- a/src/Umbraco.Web.BackOffice/ModelsBuilder/DashboardReport.cs +++ b/src/Umbraco.Web.BackOffice/ModelsBuilder/DashboardReport.cs @@ -25,7 +25,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelsBuilder public bool AreModelsOutOfDate() => _outOfDateModels.IsOutOfDate; - public string LastError() => _mbErrors.GetLastError(); + public string? LastError() => _mbErrors.GetLastError(); public string Text() { diff --git a/src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardController.cs b/src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardController.cs index 5e33fa1835..ec81197733 100644 --- a/src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardController.cs +++ b/src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardController.cs @@ -101,7 +101,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelsBuilder public bool Success { get; set; } [DataMember(Name = "message")] - public string Message { get; set; } + public string? Message { get; set; } } [DataContract] @@ -111,7 +111,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelsBuilder public ModelsMode Mode { get; set; } [DataMember(Name = "text")] - public string Text { get; set; } + public string? Text { get; set; } [DataMember(Name = "canGenerate")] public bool CanGenerate { get; set; } @@ -120,7 +120,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelsBuilder public bool OutOfDateModels { get; set; } [DataMember(Name = "lastError")] - public string LastError { get; set; } + public string? LastError { get; set; } } public enum OutOfDateType diff --git a/src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardProvider.cs b/src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardProvider.cs index 46fd379fa3..78bd7568a5 100644 --- a/src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardProvider.cs +++ b/src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardProvider.cs @@ -13,7 +13,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelsBuilder _linkGenerator = linkGenerator; } - public string GetUrl() => + public string? GetUrl() => _linkGenerator.GetUmbracoApiServiceBaseUrl(controller => controller.BuildModels()); } diff --git a/src/Umbraco.Web.BackOffice/PropertyEditors/RteEmbedController.cs b/src/Umbraco.Web.BackOffice/PropertyEditors/RteEmbedController.cs index 08a35b6c6d..a42c6cd0cc 100644 --- a/src/Umbraco.Web.BackOffice/PropertyEditors/RteEmbedController.cs +++ b/src/Umbraco.Web.BackOffice/PropertyEditors/RteEmbedController.cs @@ -28,7 +28,7 @@ namespace Umbraco.Cms.Web.BackOffice.PropertyEditors { var result = new OEmbedResult(); var foundMatch = false; - IEmbedProvider matchedProvider = null; + IEmbedProvider? matchedProvider = null; foreach (var provider in _embedCollection) { @@ -58,7 +58,7 @@ namespace Umbraco.Cms.Web.BackOffice.PropertyEditors try { result.SupportsDimensions = true; - result.Markup = matchedProvider.GetMarkup(url, width, height); + result.Markup = matchedProvider?.GetMarkup(url, width, height); result.OEmbedStatus = OEmbedStatus.Success; } catch(Exception ex) diff --git a/src/Umbraco.Web.BackOffice/PropertyEditors/TagsDataController.cs b/src/Umbraco.Web.BackOffice/PropertyEditors/TagsDataController.cs index be20144533..3775170b1f 100644 --- a/src/Umbraco.Web.BackOffice/PropertyEditors/TagsDataController.cs +++ b/src/Umbraco.Web.BackOffice/PropertyEditors/TagsDataController.cs @@ -47,10 +47,10 @@ namespace Umbraco.Cms.Web.BackOffice.PropertyEditors { //TODO: add the query to TagQuery + the tag service, this is ugly but all we can do for now. //currently we are post filtering this :( but works for now - result = result.Where(x => x.Text.InvariantContains(query)); + result = result.Where(x => x?.Text?.InvariantContains(query!) ?? false); } - return result.OrderBy(x => x.Text); + return result.WhereNotNull().OrderBy(x => x.Text); } } } diff --git a/src/Umbraco.Web.BackOffice/PropertyEditors/Validation/ContentPropertyValidationResult.cs b/src/Umbraco.Web.BackOffice/PropertyEditors/Validation/ContentPropertyValidationResult.cs index 5cd434bd2d..c3495bc9a5 100644 --- a/src/Umbraco.Web.BackOffice/PropertyEditors/Validation/ContentPropertyValidationResult.cs +++ b/src/Umbraco.Web.BackOffice/PropertyEditors/Validation/ContentPropertyValidationResult.cs @@ -32,7 +32,7 @@ namespace Umbraco.Cms.Web.BackOffice.PropertyEditors.Validation /// /// There can be nested results for complex editors that contain other editors /// - public ComplexEditorValidationResult ComplexEditorResults { get; } + public ComplexEditorValidationResult? ComplexEditorResults { get; } /// /// Return the if is null, else the serialized diff --git a/src/Umbraco.Web.BackOffice/PropertyEditors/Validation/ValidationResultConverter.cs b/src/Umbraco.Web.BackOffice/PropertyEditors/Validation/ValidationResultConverter.cs index 4be6b17b32..38d62748a6 100644 --- a/src/Umbraco.Web.BackOffice/PropertyEditors/Validation/ValidationResultConverter.cs +++ b/src/Umbraco.Web.BackOffice/PropertyEditors/Validation/ValidationResultConverter.cs @@ -39,12 +39,12 @@ namespace Umbraco.Cms.Web.BackOffice.PropertyEditors.Validation public override bool CanConvert(Type objectType) => typeof(ValidationResult).IsAssignableFrom(objectType); - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { var camelCaseSerializer = new JsonSerializer { @@ -54,7 +54,7 @@ namespace Umbraco.Cms.Web.BackOffice.PropertyEditors.Validation foreach (var c in serializer.Converters) camelCaseSerializer.Converters.Add(c); - var validationResult = (ValidationResult)value; + var validationResult = (ValidationResult?)value; if (validationResult is ComplexEditorValidationResult nestedResult && nestedResult.ValidationResults.Count > 0) { @@ -139,12 +139,12 @@ namespace Umbraco.Cms.Web.BackOffice.PropertyEditors.Validation } var jo = new JObject(); - if (!validationResult.ErrorMessage.IsNullOrWhiteSpace()) + if (!validationResult?.ErrorMessage.IsNullOrWhiteSpace() ?? false) { - var errObj = JToken.FromObject(validationResult.ErrorMessage, camelCaseSerializer); + var errObj = JToken.FromObject(validationResult.ErrorMessage!, camelCaseSerializer); jo.Add("errorMessage", errObj); } - if (validationResult.MemberNames.Any()) + if (validationResult?.MemberNames.Any() ?? false) { var memberNamesObj = JToken.FromObject(validationResult.MemberNames, camelCaseSerializer); jo.Add("memberNames", memberNamesObj); diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeAntiforgery.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeAntiforgery.cs index 7c04ddf716..f70eff32c1 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeAntiforgery.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeAntiforgery.cs @@ -47,12 +47,12 @@ namespace Umbraco.Cms.Web.BackOffice.Security } /// - public async Task> ValidateRequestAsync(HttpContext httpContext) + public async Task> ValidateRequestAsync(HttpContext httpContext) { try { await _internalAntiForgery.ValidateRequestAsync(httpContext); - return Attempt.Succeed(); + return Attempt.Succeed(); } catch (Exception ex) { diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs index 8d4e04d2c0..123622c9bb 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs @@ -18,11 +18,11 @@ namespace Umbraco.Cms.Web.BackOffice.Security public BackOfficeAuthenticationBuilder( IServiceCollection services, - Action loginProviderOptions = null) + Action? loginProviderOptions = null) : base(services) => _loginProviderOptions = loginProviderOptions ?? (x => { }); - public string SchemeForBackOffice(string scheme) + public string? SchemeForBackOffice(string scheme) => scheme?.EnsureStartsWith(Constants.Security.BackOfficeExternalAuthenticationTypePrefix); /// @@ -34,7 +34,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security /// /// /// - public override AuthenticationBuilder AddRemoteScheme(string authenticationScheme, string displayName, Action configureOptions) + public override AuthenticationBuilder AddRemoteScheme(string authenticationScheme, string? displayName, Action? configureOptions) { // Validate that the prefix is set if (!authenticationScheme.StartsWith(Constants.Security.BackOfficeExternalAuthenticationTypePrefix)) diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeCookieManager.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeCookieManager.cs index 48b0d28dd2..1479e3b2be 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeCookieManager.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeCookieManager.cs @@ -23,7 +23,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security { private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IRuntimeState _runtime; - private readonly string[] _explicitPaths; + private readonly string[]? _explicitPaths; private readonly UmbracoRequestPaths _umbracoRequestPaths; private readonly IBasicAuthService _basicAuthService; @@ -45,7 +45,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security public BackOfficeCookieManager( IUmbracoContextAccessor umbracoContextAccessor, IRuntimeState runtime, - IEnumerable explicitPaths, + IEnumerable? explicitPaths, UmbracoRequestPaths umbracoRequestPaths, IBasicAuthService basicAuthService) { @@ -106,7 +106,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security /// Explicitly implement this so that we filter the request /// /// - string Microsoft.AspNetCore.Authentication.Cookies.ICookieManager.GetRequestCookie(HttpContext context, string key) + string? Microsoft.AspNetCore.Authentication.Cookies.ICookieManager.GetRequestCookie(HttpContext context, string key) { var absPath = context.Request.Path; if (!_umbracoContextAccessor.TryGetUmbracoContext(out _) || _umbracoRequestPaths.IsClientSideRequest(absPath)) diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternaLoginProviderScheme.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternaLoginProviderScheme.cs index 2732338426..86dcac19ed 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternaLoginProviderScheme.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternaLoginProviderScheme.cs @@ -7,7 +7,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security { public BackOfficeExternaLoginProviderScheme( BackOfficeExternalLoginProvider externalLoginProvider, - AuthenticationScheme authenticationScheme) + AuthenticationScheme? authenticationScheme) { ExternalLoginProvider = externalLoginProvider ?? throw new ArgumentNullException(nameof(externalLoginProvider)); AuthenticationScheme = authenticationScheme ?? throw new ArgumentNullException(nameof(authenticationScheme)); diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProvider.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProvider.cs index 9e78917087..e8223bd2b2 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProvider.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProvider.cs @@ -28,8 +28,8 @@ namespace Umbraco.Cms.Web.BackOffice.Security public BackOfficeExternalLoginProviderOptions Options { get; } - public override bool Equals(object obj) => Equals(obj as BackOfficeExternalLoginProvider); - public bool Equals(BackOfficeExternalLoginProvider other) => other != null && AuthenticationType == other.AuthenticationType; + public override bool Equals(object? obj) => Equals(obj as BackOfficeExternalLoginProvider); + public bool Equals(BackOfficeExternalLoginProvider? other) => other != null && AuthenticationType == other.AuthenticationType; public override int GetHashCode() => HashCode.Combine(AuthenticationType); } diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs index d58f1cea17..3d426c1aae 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs @@ -8,10 +8,10 @@ namespace Umbraco.Cms.Web.BackOffice.Security public BackOfficeExternalLoginProviderOptions( string buttonStyle, string icon, - ExternalSignInAutoLinkOptions autoLinkOptions = null, + ExternalSignInAutoLinkOptions? autoLinkOptions = null, bool denyLocalLogin = false, bool autoRedirectLoginToExternalProvider = false, - string customBackOfficeView = null) + string? customBackOfficeView = null) { ButtonStyle = buttonStyle; Icon = icon; @@ -55,6 +55,6 @@ namespace Umbraco.Cms.Web.BackOffice.Security /// If this view is specified it is 100% up to the user to render the html responsible for rendering the link/un-link buttons along with showing any errors /// that occur. This overrides what Umbraco normally does by default. /// - public string CustomBackOfficeView { get; set; } + public string? CustomBackOfficeView { get; set; } } } diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviders.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviders.cs index 4c9799b9a4..3058a10ea8 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviders.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviders.cs @@ -22,15 +22,15 @@ namespace Umbraco.Cms.Web.BackOffice.Security } /// - public async Task GetAsync(string authenticationType) + public async Task GetAsync(string authenticationType) { - if (!_externalLogins.TryGetValue(authenticationType, out BackOfficeExternalLoginProvider provider)) + if (!_externalLogins.TryGetValue(authenticationType, out BackOfficeExternalLoginProvider? provider)) { return null; } // get the associated scheme - AuthenticationScheme associatedScheme = await _authenticationSchemeProvider.GetSchemeAsync(provider.AuthenticationType); + AuthenticationScheme? associatedScheme = await _authenticationSchemeProvider.GetSchemeAsync(provider.AuthenticationType); if (associatedScheme == null) { @@ -41,7 +41,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security } /// - public string GetAutoLoginProvider() + public string? GetAutoLoginProvider() { var found = _externalLogins.Values.Where(x => x.Options.AutoRedirectLoginToExternalProvider).ToList(); return found.Count > 0 ? found[0].AuthenticationType : null; @@ -54,7 +54,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security foreach (BackOfficeExternalLoginProvider login in _externalLogins.Values) { // get the associated scheme - AuthenticationScheme associatedScheme = await _authenticationSchemeProvider.GetSchemeAsync(login.AuthenticationType); + AuthenticationScheme? associatedScheme = await _authenticationSchemeProvider.GetSchemeAsync(login.AuthenticationType); providersWithSchemes.Add(new BackOfficeExternaLoginProviderScheme(login, associatedScheme)); } diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginsBuilder.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginsBuilder.cs index cab3ea10d1..6d8a4c59a7 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginsBuilder.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginsBuilder.cs @@ -21,9 +21,9 @@ namespace Umbraco.Cms.Web.BackOffice.Security /// /// /// - public BackOfficeExternalLoginsBuilder AddBackOfficeLogin( + public BackOfficeExternalLoginsBuilder AddBackOfficeLogin( Action build, - Action loginProviderOptions = null) + Action? loginProviderOptions = null) { build(new BackOfficeAuthenticationBuilder(_services, loginProviderOptions)); return this; diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeSecureDataFormat.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeSecureDataFormat.cs index d2064a8ed9..6e5155d5d3 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeSecureDataFormat.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeSecureDataFormat.cs @@ -20,7 +20,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security _ticketDataFormat = ticketDataFormat ?? throw new ArgumentNullException(nameof(ticketDataFormat)); } - public string Protect(AuthenticationTicket data, string purpose) + public string Protect(AuthenticationTicket data, string? purpose) { // create a new ticket based on the passed in tickets details, however, we'll adjust the expires utc based on the specified timeout mins var ticket = new AuthenticationTicket(data.Principal, @@ -39,16 +39,16 @@ namespace Umbraco.Cms.Web.BackOffice.Security public string Protect(AuthenticationTicket data) => Protect(data, string.Empty); - public AuthenticationTicket Unprotect(string protectedText) => Unprotect(protectedText, string.Empty); + public AuthenticationTicket? Unprotect(string? protectedText) => Unprotect(protectedText, string.Empty); /// /// Un-protects the cookie /// /// /// - public AuthenticationTicket Unprotect(string protectedText, string purpose) + public AuthenticationTicket? Unprotect(string? protectedText, string? purpose) { - AuthenticationTicket decrypt; + AuthenticationTicket? decrypt; try { decrypt = _ticketDataFormat.Unprotect(protectedText); @@ -59,8 +59,8 @@ namespace Umbraco.Cms.Web.BackOffice.Security return null; } - var identity = (ClaimsIdentity)decrypt.Principal.Identity; - if (!identity.VerifyBackOfficeIdentity(out ClaimsIdentity verifiedIdentity)) + var identity = (ClaimsIdentity?)decrypt.Principal.Identity; + if (identity is null || !identity.VerifyBackOfficeIdentity(out ClaimsIdentity? verifiedIdentity)) { return null; } diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeSessionIdValidator.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeSessionIdValidator.cs index c085628a62..1964b7274c 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeSessionIdValidator.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeSessionIdValidator.cs @@ -54,7 +54,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security return; } - var valid = await ValidateSessionAsync(validateInterval, context.HttpContext, context.Options.CookieManager, _systemClock, context.Properties.IssuedUtc, context.Principal.Identity as ClaimsIdentity); + var valid = await ValidateSessionAsync(validateInterval, context.HttpContext, context.Options.CookieManager, _systemClock, context.Properties.IssuedUtc, context.Principal?.Identity as ClaimsIdentity); if (valid == false) { @@ -69,7 +69,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security Microsoft.AspNetCore.Authentication.Cookies.ICookieManager cookieManager, ISystemClock systemClock, DateTimeOffset? authTicketIssueDate, - ClaimsIdentity currentIdentity) + ClaimsIdentity? currentIdentity) { if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); if (cookieManager == null) throw new ArgumentNullException(nameof(cookieManager)); diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs index 955af96972..463de51ff7 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs @@ -125,7 +125,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security /// /// /// - protected override async Task HandleSignIn(BackOfficeIdentityUser user, string username, SignInResult result) + protected override async Task HandleSignIn(BackOfficeIdentityUser? user, string? username, SignInResult result) { result = await base.HandleSignIn(user, username, result); @@ -138,11 +138,11 @@ namespace Umbraco.Cms.Web.BackOffice.Security } else if (result.IsLockedOut) { - _userManager.NotifyAccountLocked(Context.User, user.Id); + _userManager.NotifyAccountLocked(Context.User, user?.Id); } else if (result.RequiresTwoFactor) { - _userManager.NotifyLoginRequiresVerification(Context.User, user.Id); + _userManager.NotifyLoginRequiresVerification(Context.User, user?.Id); } else if (!result.Succeeded || result.IsNotAllowed) { @@ -161,7 +161,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security /// /// /// - private async Task AutoLinkAndSignInExternalAccount(ExternalLoginInfo loginInfo, ExternalSignInAutoLinkOptions autoLinkOptions) + private async Task AutoLinkAndSignInExternalAccount(ExternalLoginInfo loginInfo, ExternalSignInAutoLinkOptions? autoLinkOptions) { // If there are no autolink options then the attempt is failed (user does not exist) if (autoLinkOptions == null || !autoLinkOptions.AutoLinkExternalAccount) diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs index 75cd5ab5a1..d69456efa0 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs @@ -53,9 +53,9 @@ namespace Umbraco.Cms.Web.BackOffice.Security private static string FormatEmail(IMembershipUser user) => user == null ? string.Empty : user.Email.IsNullOrWhiteSpace() ? "" : $"<{user.Email}>"; - private void WriteAudit(string performingId, string affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null) + private void WriteAudit(string performingId, string? affectedId, string ipAddress, string eventType, string eventDetails, string? affectedDetails = null) { - IUser performingUser = null; + IUser? performingUser = null; if (int.TryParse(performingId, NumberStyles.Integer, CultureInfo.InvariantCulture, out int asInt)) { performingUser = _userService.GetUserById(asInt); @@ -78,11 +78,11 @@ namespace Umbraco.Cms.Web.BackOffice.Security WriteAudit(performingIdAsInt, performingDetails, affectedIdAsInt, ipAddress, eventType, eventDetails, affectedDetails); } - private void WriteAudit(int performingId, string performingDetails, int affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null) + private void WriteAudit(int performingId, string performingDetails, int affectedId, string ipAddress, string eventType, string eventDetails, string? affectedDetails = null) { if (affectedDetails == null) { - IUser affectedUser = _userService.GetUserById(affectedId); + IUser? affectedUser = _userService.GetUserById(affectedId); affectedDetails = affectedUser == null ? $"User UNKNOWN:{affectedId}" : $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}"; diff --git a/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs b/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs index 43086f8c63..c49c73b856 100644 --- a/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs +++ b/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs @@ -141,7 +141,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security // Same goes for the signinmanager IBackOfficeSignInManager signInManager = ctx.HttpContext.RequestServices.GetRequiredService(); - ClaimsIdentity backOfficeIdentity = ctx.Principal.GetUmbracoIdentity(); + ClaimsIdentity? backOfficeIdentity = ctx.Principal?.GetUmbracoIdentity(); if (backOfficeIdentity == null) { ctx.RejectPrincipal(); @@ -149,14 +149,14 @@ namespace Umbraco.Cms.Web.BackOffice.Security } // ensure the thread culture is set - backOfficeIdentity.EnsureCulture(); + backOfficeIdentity?.EnsureCulture(); EnsureTicketRenewalIfKeepUserLoggedIn(ctx); // add or update a claim to track when the cookie expires, we use this to track time remaining - backOfficeIdentity.AddOrUpdateClaim(new Claim( + backOfficeIdentity?.AddOrUpdateClaim(new Claim( Constants.Security.TicketExpiresClaimType, - ctx.Properties.ExpiresUtc.Value.ToString("o"), + ctx.Properties.ExpiresUtc!.Value.ToString("o"), ClaimValueTypes.DateTime, Constants.Security.BackOfficeAuthenticationType, Constants.Security.BackOfficeAuthenticationType, @@ -188,13 +188,13 @@ namespace Umbraco.Cms.Web.BackOffice.Security OnSigningIn = ctx => { // occurs when sign in is successful but before the ticket is written to the outbound cookie - ClaimsIdentity backOfficeIdentity = ctx.Principal.GetUmbracoIdentity(); + ClaimsIdentity? backOfficeIdentity = ctx.Principal?.GetUmbracoIdentity(); if (backOfficeIdentity != null) { // generate a session id and assign it // create a session token - if we are configured and not in an upgrade state then use the db, otherwise just generate one Guid session = _runtimeState.Level == RuntimeLevel.Run - ? _userService.CreateLoginSession(backOfficeIdentity.GetId().Value, _ipResolver.GetCurrentRequestIpAddress()) + ? _userService.CreateLoginSession(backOfficeIdentity.GetId()!.Value, _ipResolver.GetCurrentRequestIpAddress()) : Guid.NewGuid(); // add our session claim @@ -221,7 +221,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security if (ctx.HttpContext?.User?.Identity != null) { var claimsIdentity = ctx.HttpContext.User.Identity as ClaimsIdentity; - var sessionId = claimsIdentity.FindFirstValue(Constants.Security.SessionIdClaimType); + var sessionId = claimsIdentity?.FindFirstValue(Constants.Security.SessionIdClaimType); if (sessionId.IsNullOrWhiteSpace() == false && Guid.TryParse(sessionId, out Guid guidSession)) { _userService.ClearLoginSession(guidSession); @@ -240,7 +240,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security }; foreach (var cookie in cookies) { - ctx.Options.CookieManager.DeleteCookie(ctx.HttpContext, cookie, new CookieOptions + ctx.Options.CookieManager.DeleteCookie(ctx.HttpContext!, cookie, new CookieOptions { Path = "/" }); diff --git a/src/Umbraco.Web.BackOffice/Security/ExternalSignInAutoLinkOptions.cs b/src/Umbraco.Web.BackOffice/Security/ExternalSignInAutoLinkOptions.cs index 1f5a7fad33..add8eca7b8 100644 --- a/src/Umbraco.Web.BackOffice/Security/ExternalSignInAutoLinkOptions.cs +++ b/src/Umbraco.Web.BackOffice/Security/ExternalSignInAutoLinkOptions.cs @@ -22,8 +22,8 @@ namespace Umbraco.Cms.Web.BackOffice.Security /// public ExternalSignInAutoLinkOptions( bool autoLinkExternalAccount = false, - string[] defaultUserGroups = null, - string defaultCulture = null, + string[]? defaultUserGroups = null, + string? defaultCulture = null, bool allowManualLinking = true) { DefaultUserGroups = defaultUserGroups ?? new[] { SecurityConstants.EditorGroupAlias }; @@ -36,14 +36,14 @@ namespace Umbraco.Cms.Web.BackOffice.Security /// A callback executed during account auto-linking and before the user is persisted /// [IgnoreDataMember] - public Action OnAutoLinking { get; set; } + public Action? OnAutoLinking { get; set; } /// /// A callback executed during every time a user authenticates using an external login. /// returns a boolean indicating if sign in should continue or not. /// [IgnoreDataMember] - public Func OnExternalLogin { get; set; } + public Func? OnExternalLogin { get; set; } /// /// Gets a value indicating whether flag indicating if logging in with the external provider should auto-link/create a local user @@ -61,7 +61,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security /// public string[] DefaultUserGroups { get; } - private readonly string _defaultCulture; + private readonly string? _defaultCulture; /// /// The default Culture to use for auto-linking users diff --git a/src/Umbraco.Web.BackOffice/Security/IBackOfficeAntiforgery.cs b/src/Umbraco.Web.BackOffice/Security/IBackOfficeAntiforgery.cs index 97200b6a37..59b81ef35d 100644 --- a/src/Umbraco.Web.BackOffice/Security/IBackOfficeAntiforgery.cs +++ b/src/Umbraco.Web.BackOffice/Security/IBackOfficeAntiforgery.cs @@ -15,7 +15,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security /// /// /// - Task> ValidateRequestAsync(HttpContext httpContext); + Task> ValidateRequestAsync(HttpContext httpContext); /// /// Generates tokens to use for the cookie and header antiforgery values diff --git a/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs b/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs index 2426cfcf4d..0d242847b3 100644 --- a/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs +++ b/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security /// /// /// - Task GetAsync(string authenticationType); + Task GetAsync(string authenticationType); /// /// Get all registered @@ -27,7 +27,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security /// /// /// - string GetAutoLoginProvider(); + string? GetAutoLoginProvider(); /// /// Returns true if there is any external provider that has the Deny Local Login option configured diff --git a/src/Umbraco.Web.BackOffice/Security/IBackOfficeTwoFactorOptions.cs b/src/Umbraco.Web.BackOffice/Security/IBackOfficeTwoFactorOptions.cs index 291781fb23..9c060714d7 100644 --- a/src/Umbraco.Web.BackOffice/Security/IBackOfficeTwoFactorOptions.cs +++ b/src/Umbraco.Web.BackOffice/Security/IBackOfficeTwoFactorOptions.cs @@ -10,7 +10,7 @@ /// /// /// - string GetTwoFactorView(string username); + string? GetTwoFactorView(string username); } } diff --git a/src/Umbraco.Web.BackOffice/Security/IPasswordChanger.cs b/src/Umbraco.Web.BackOffice/Security/IPasswordChanger.cs index 95be44f066..77053602be 100644 --- a/src/Umbraco.Web.BackOffice/Security/IPasswordChanger.cs +++ b/src/Umbraco.Web.BackOffice/Security/IPasswordChanger.cs @@ -7,7 +7,7 @@ namespace Umbraco.Cms.Web.Common.Security { public interface IPasswordChanger where TUser : UmbracoIdentityUser { - public Task> ChangePasswordWithIdentityAsync( + public Task> ChangePasswordWithIdentityAsync( ChangingPasswordModel passwordModel, IUmbracoUserManager userMgr); } diff --git a/src/Umbraco.Web.BackOffice/Security/NoopBackOfficeTwoFactorOptions.cs b/src/Umbraco.Web.BackOffice/Security/NoopBackOfficeTwoFactorOptions.cs index 05cc7970b4..9e33b05757 100644 --- a/src/Umbraco.Web.BackOffice/Security/NoopBackOfficeTwoFactorOptions.cs +++ b/src/Umbraco.Web.BackOffice/Security/NoopBackOfficeTwoFactorOptions.cs @@ -2,7 +2,7 @@ { public class NoopBackOfficeTwoFactorOptions : IBackOfficeTwoFactorOptions { - public string GetTwoFactorView(string username) => null; + public string? GetTwoFactorView(string username) => null; } } diff --git a/src/Umbraco.Web.BackOffice/Security/PasswordChanger.cs b/src/Umbraco.Web.BackOffice/Security/PasswordChanger.cs index 18e3a4db48..5caf477a5f 100644 --- a/src/Umbraco.Web.BackOffice/Security/PasswordChanger.cs +++ b/src/Umbraco.Web.BackOffice/Security/PasswordChanger.cs @@ -34,7 +34,7 @@ namespace Umbraco.Cms.Web.Common.Security /// The identity manager to use to update the password /// Create an adapter to pass through everything - adapting the member into a user for this functionality /// The outcome of the password changed model - public async Task> ChangePasswordWithIdentityAsync( + public async Task> ChangePasswordWithIdentityAsync( ChangingPasswordModel changingPasswordModel, IUmbracoUserManager userMgr) { diff --git a/src/Umbraco.Web.BackOffice/Services/IconService.cs b/src/Umbraco.Web.BackOffice/Services/IconService.cs index 5c9dc0e8eb..e4ad1fe053 100644 --- a/src/Umbraco.Web.BackOffice/Services/IconService.cs +++ b/src/Umbraco.Web.BackOffice/Services/IconService.cs @@ -32,10 +32,10 @@ namespace Umbraco.Cms.Web.BackOffice.Services } /// - public IReadOnlyDictionary GetIcons() => GetIconDictionary(); + public IReadOnlyDictionary? GetIcons() => GetIconDictionary(); /// - public IconModel GetIcon(string iconName) + public IconModel? GetIcon(string iconName) { if (iconName.IsNullOrWhiteSpace()) { @@ -43,7 +43,7 @@ namespace Umbraco.Cms.Web.BackOffice.Services } var allIconModels = GetIconDictionary(); - if (allIconModels.ContainsKey(iconName)) + if (allIconModels?.ContainsKey(iconName) ?? false) { return new IconModel { @@ -60,7 +60,7 @@ namespace Umbraco.Cms.Web.BackOffice.Services /// /// /// - private IconModel GetIcon(FileInfo fileInfo) + private IconModel? GetIcon(FileInfo fileInfo) { return fileInfo == null || string.IsNullOrWhiteSpace(fileInfo.Name) ? null @@ -73,7 +73,7 @@ namespace Umbraco.Cms.Web.BackOffice.Services /// /// /// - private IconModel CreateIconModel(string iconName, string iconPath) + private IconModel? CreateIconModel(string iconName, string iconPath) { try { @@ -106,7 +106,7 @@ namespace Umbraco.Cms.Web.BackOffice.Services // iterate sub directories of app plugins foreach (var dir in appPlugins.EnumerateDirectories()) { - // AppPluginIcons path was previoulsy the wrong case, so we first check for the prefered directory + // AppPluginIcons path was previoulsy the wrong case, so we first check for the prefered directory // and then check the legacy directory. var iconPath = _hostingEnvironment.MapPathContentRoot($"{Constants.SystemDirectories.AppPlugins}/{dir.Name}{Constants.SystemDirectories.PluginIcons}"); var iconPathExists = Directory.Exists(iconPath); @@ -136,16 +136,16 @@ namespace Umbraco.Cms.Web.BackOffice.Services private class CaseInsensitiveFileInfoComparer : IEqualityComparer { - public bool Equals(FileInfo one, FileInfo two) => StringComparer.InvariantCultureIgnoreCase.Equals(one.Name, two.Name); + public bool Equals(FileInfo? one, FileInfo? two) => StringComparer.InvariantCultureIgnoreCase.Equals(one?.Name, two?.Name); public int GetHashCode(FileInfo item) => StringComparer.InvariantCultureIgnoreCase.GetHashCode(item.Name); } - private IReadOnlyDictionary GetIconDictionary() => _cache.GetCacheItem( + private IReadOnlyDictionary? GetIconDictionary() => _cache.GetCacheItem( $"{typeof(IconService).FullName}.{nameof(GetIconDictionary)}", () => GetAllIconsFiles() .Select(GetIcon) - .Where(i => i != null) + .WhereNotNull() .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .ToDictionary(g => g.Key, g => g.First().SvgString, StringComparer.OrdinalIgnoreCase) ); diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs index 1e3a3ba726..5b51ddda55 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs @@ -90,9 +90,9 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// - protected override TreeNode? GetSingleTreeNode(IEntitySlim entity, string parentId, FormCollection queryStrings) + protected override TreeNode? GetSingleTreeNode(IEntitySlim entity, string parentId, FormCollection? queryStrings) { - var culture = queryStrings["culture"].ToString(); + var culture = queryStrings?["culture"].ToString(); var allowedUserOptions = GetAllowedUserMenuItemsForNode(entity); if (CanUserAccessNode(entity, allowedUserOptions, culture)) diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs index e54e695be3..028e318b7a 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs @@ -68,7 +68,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// /// /// - public ActionResult GetTreeNode([FromRoute] string id, [ModelBinder(typeof(HttpQueryStringModelBinder))] FormCollection queryStrings) + public ActionResult GetTreeNode([FromRoute] string id, [ModelBinder(typeof(HttpQueryStringModelBinder))] FormCollection? queryStrings) { int asInt; Guid asGuid = Guid.Empty; @@ -123,7 +123,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees return node; } - protected abstract TreeNode? GetSingleTreeNode(IEntitySlim entity, string parentId, FormCollection queryStrings); + protected abstract TreeNode? GetSingleTreeNode(IEntitySlim entity, string parentId, FormCollection? queryStrings); /// /// Returns a for the and diff --git a/src/Umbraco.Web.BackOffice/Trees/ITreeNodeController.cs b/src/Umbraco.Web.BackOffice/Trees/ITreeNodeController.cs index ccb8b7508c..2faffdfa7f 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ITreeNodeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ITreeNodeController.cs @@ -18,7 +18,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// ActionResult GetTreeNode( string id, - [ModelBinder(typeof(HttpQueryStringModelBinder))] FormCollection queryStrings + [ModelBinder(typeof(HttpQueryStringModelBinder))] FormCollection? queryStrings ); } } diff --git a/src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs index 8033d0975b..b8c057e243 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs @@ -76,7 +76,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// /// /// - protected override TreeNode GetSingleTreeNode(IEntitySlim entity, string parentId, FormCollection queryStrings) + protected override TreeNode GetSingleTreeNode(IEntitySlim entity, string parentId, FormCollection? queryStrings) { var node = CreateTreeNode( entity, diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs index 8b17abbbd7..4b997b44de 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs @@ -56,7 +56,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// /// Gets an individual tree node /// - public ActionResult GetTreeNode([FromRoute]string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection queryStrings) + public ActionResult GetTreeNode([FromRoute]string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection? queryStrings) { ActionResult node = GetSingleTreeNode(id, queryStrings); @@ -74,7 +74,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees return node; } - protected ActionResult GetSingleTreeNode(string id, FormCollection queryStrings) + protected ActionResult GetSingleTreeNode(string id, FormCollection? queryStrings) { Guid asGuid; if (Guid.TryParse(id, out asGuid) == false) diff --git a/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs b/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs index 03e009c281..a09d3a2cc5 100644 --- a/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs @@ -275,7 +275,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// /// /// - public TreeNode CreateTreeNode(string id, string parentId, FormCollection queryStrings, string? title, string? icon) + public TreeNode CreateTreeNode(string id, string parentId, FormCollection? queryStrings, string? title, string? icon) { var jsonUrl = Url.GetTreeUrl(_apiControllers, GetType(), id, queryStrings); var menuUrl = Url.GetMenuUrl(_apiControllers, GetType(), id, queryStrings); @@ -315,7 +315,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// /// /// - public TreeNode CreateTreeNode(IEntitySlim entity, Guid entityObjectType, string parentId, FormCollection queryStrings, bool hasChildren) + public TreeNode CreateTreeNode(IEntitySlim entity, Guid entityObjectType, string parentId, FormCollection? queryStrings, bool hasChildren) { var contentTypeIcon = entity is IContentEntitySlim contentEntity ? contentEntity.ContentTypeIcon : null; var treeNode = CreateTreeNode(entity.Id.ToInvariantString(), parentId, queryStrings, entity.Name, contentTypeIcon); @@ -393,7 +393,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// /// /// - public TreeNode CreateTreeNode(string id, string parentId, FormCollection queryStrings, string? title, string icon, bool hasChildren, string? routePath, Udi udi) + public TreeNode CreateTreeNode(string id, string parentId, FormCollection? queryStrings, string? title, string icon, bool hasChildren, string? routePath, Udi udi) { var treeNode = CreateTreeNode(id, parentId, queryStrings, title, icon); treeNode.HasChildren = hasChildren; diff --git a/src/Umbraco.Web.BackOffice/Trees/UrlHelperExtensions.cs b/src/Umbraco.Web.BackOffice/Trees/UrlHelperExtensions.cs index dd2de371c7..a80ea0e3c8 100644 --- a/src/Umbraco.Web.BackOffice/Trees/UrlHelperExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Trees/UrlHelperExtensions.cs @@ -41,26 +41,26 @@ namespace Umbraco.Cms.Web.BackOffice.Trees return sb.ToString().TrimEnd(","); } - public static string GetTreeUrl(this IUrlHelper urlHelper, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, Type treeType, string nodeId, FormCollection queryStrings) + public static string GetTreeUrl(this IUrlHelper urlHelper, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, Type treeType, string nodeId, FormCollection? queryStrings) { var actionUrl = urlHelper.GetUmbracoApiService(umbracoApiControllerTypeCollection, "GetNodes", treeType)? .EnsureEndsWith('?'); //now we need to append the query strings - actionUrl += "id=" + nodeId.EnsureEndsWith('&') + queryStrings.ToQueryString("id", + actionUrl += "id=" + nodeId.EnsureEndsWith('&') + queryStrings?.ToQueryString("id", //Always ignore the custom start node id when generating URLs for tree nodes since this is a custom once-only parameter // that should only ever be used when requesting a tree to render (root), not a tree node TreeQueryStringParameters.StartNodeId); return actionUrl; } - public static string GetMenuUrl(this IUrlHelper urlHelper, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, Type treeType, string nodeId, FormCollection queryStrings) + public static string GetMenuUrl(this IUrlHelper urlHelper, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, Type treeType, string nodeId, FormCollection? queryStrings) { var actionUrl = urlHelper.GetUmbracoApiService(umbracoApiControllerTypeCollection, "GetMenu", treeType)? .EnsureEndsWith('?'); //now we need to append the query strings - actionUrl += "id=" + nodeId.EnsureEndsWith('&') + queryStrings.ToQueryString("id"); + actionUrl += "id=" + nodeId.EnsureEndsWith('&') + queryStrings?.ToQueryString("id"); return actionUrl; } } diff --git a/src/Umbraco.Web.Common/Extensions/EndpointRouteBuilderExtensions.cs b/src/Umbraco.Web.Common/Extensions/EndpointRouteBuilderExtensions.cs index e984c3ae3f..f60ca6fe00 100644 --- a/src/Umbraco.Web.Common/Extensions/EndpointRouteBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/EndpointRouteBuilderExtensions.cs @@ -16,7 +16,7 @@ namespace Umbraco.Extensions Type controllerType, string rootSegment, string areaName, - string prefixPathSegment, + string? prefixPathSegment, string defaultAction = "Index", bool includeControllerNameInRoute = true, object? constraints = null) @@ -72,7 +72,7 @@ namespace Umbraco.Extensions this IEndpointRouteBuilder endpoints, string rootSegment, string areaName, - string prefixPathSegment, + string? prefixPathSegment, string defaultAction = "Index", bool includeControllerNameInRoute = true, object? constraints = null) @@ -98,14 +98,14 @@ namespace Umbraco.Extensions /// public static void MapUmbracoApiRoute( this IEndpointRouteBuilder endpoints, - Type controllerType, + Type? controllerType, string rootSegment, - string areaName, + string? areaName, bool isBackOffice, string defaultAction = "Index", object? constraints = null) { - string prefixPathSegment = isBackOffice + string? prefixPathSegment = isBackOffice ? areaName.IsNullOrWhiteSpace() ? $"{Cms.Core.Constants.Web.Mvc.BackOfficePathSegment}/Api" : $"{Cms.Core.Constants.Web.Mvc.BackOfficePathSegment}/{areaName}" diff --git a/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs b/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs index 2a2f82e576..d7ee7fd627 100644 --- a/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs @@ -79,7 +79,7 @@ namespace Umbraco.Extensions } - public static void SetReasonPhrase(this HttpContext httpContext, string reasonPhrase) + public static void SetReasonPhrase(this HttpContext httpContext, string? reasonPhrase) { //TODO we should update this behavior, as HTTP2 do not have ReasonPhrase. Could as well be returned in body // https://github.com/aspnet/HttpAbstractions/issues/395 diff --git a/src/Umbraco.Web.Common/ModelsBuilder/IModelsBuilderDashboardProvider.cs b/src/Umbraco.Web.Common/ModelsBuilder/IModelsBuilderDashboardProvider.cs index 3ae4288128..4e3efcc90c 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/IModelsBuilderDashboardProvider.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/IModelsBuilderDashboardProvider.cs @@ -8,6 +8,6 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder { public interface IModelsBuilderDashboardProvider { - string GetUrl(); + string? GetUrl(); } } diff --git a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs index 992c676618..241a6e0ba0 100644 --- a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs +++ b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs @@ -151,7 +151,7 @@ namespace Umbraco.Cms.Web.Common.RuntimeMinification public async Task> GetCssAssetPathsAsync(string bundleName) => await _smidge.SmidgeHelper.GenerateCssUrlsAsync(bundleName, _hostingEnvironment.IsDebugMode); /// - public async Task MinifyAsync(string fileContent, AssetType assetType) + public async Task MinifyAsync(string? fileContent, AssetType assetType) { switch (assetType) { diff --git a/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs b/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs index 0335e60f4e..8bdfda4d95 100644 --- a/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs @@ -110,7 +110,7 @@ namespace Umbraco.Cms.Web.Common.Security return result; } - public override async Task ChangePasswordWithResetAsync(string userId, string token, string newPassword) + public override async Task ChangePasswordWithResetAsync(string userId, string token, string? newPassword) { IdentityResult result = await base.ChangePasswordWithResetAsync(userId, token, newPassword); if (result.Succeeded) @@ -121,7 +121,7 @@ namespace Umbraco.Cms.Web.Common.Security return result; } - public override async Task ChangePasswordAsync(BackOfficeIdentityUser user, string currentPassword, string newPassword) + public override async Task ChangePasswordAsync(BackOfficeIdentityUser user, string? currentPassword, string? newPassword) { IdentityResult result = await base.ChangePasswordAsync(user, currentPassword, newPassword); if (result.Succeeded) @@ -176,7 +176,7 @@ namespace Umbraco.Cms.Web.Common.Security return currentUserId; } - public void NotifyAccountLocked(IPrincipal? currentUser, string userId) => Notify(currentUser, + public void NotifyAccountLocked(IPrincipal? currentUser, string? userId) => Notify(currentUser, (currentUserId, ip) => new UserLockedNotification(ip, userId, currentUserId) ); @@ -196,7 +196,7 @@ namespace Umbraco.Cms.Web.Common.Security (currentUserId, ip) => new UserLoginFailedNotification(ip, userId, currentUserId) ); - public void NotifyLoginRequiresVerification(IPrincipal currentUser, string userId) => Notify(currentUser, + public void NotifyLoginRequiresVerification(IPrincipal currentUser, string? userId) => Notify(currentUser, (currentUserId, ip) => new UserLoginRequiresVerificationNotification(ip, userId, currentUserId) ); diff --git a/src/Umbraco.Web.Common/Security/IBackOfficeSignInManager.cs b/src/Umbraco.Web.Common/Security/IBackOfficeSignInManager.cs index 33da11fbf2..5e6922cb66 100644 --- a/src/Umbraco.Web.Common/Security/IBackOfficeSignInManager.cs +++ b/src/Umbraco.Web.Common/Security/IBackOfficeSignInManager.cs @@ -15,11 +15,11 @@ namespace Umbraco.Cms.Web.BackOffice.Security AuthenticationProperties ConfigureExternalAuthenticationProperties(string provider, string? redirectUrl, string? userId = null); Task ExternalLoginSignInAsync(ExternalLoginInfo loginInfo, bool isPersistent, bool bypassTwoFactor = false); Task> GetExternalAuthenticationSchemesAsync(); - Task GetExternalLoginInfoAsync(string? expectedXsrf = null); + Task GetExternalLoginInfoAsync(string? expectedXsrf = null); Task GetTwoFactorAuthenticationUserAsync(); Task PasswordSignInAsync(string userName, string password, bool isPersistent, bool lockoutOnFailure); Task SignOutAsync(); - Task SignInAsync(BackOfficeIdentityUser user, bool isPersistent, string? authenticationMethod = null); + Task SignInAsync(BackOfficeIdentityUser? user, bool isPersistent, string? authenticationMethod = null); Task CreateUserPrincipalAsync(BackOfficeIdentityUser user); Task TwoFactorSignInAsync(string? provider, string? code, bool isPersistent, bool rememberClient); Task UpdateExternalAuthenticationTokensAsync(ExternalLoginInfo externalLogin);