From 1b5225f4cddc3c5dbcc422d9088a6f2257382eac Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Tue, 23 Aug 2022 11:31:05 +0200 Subject: [PATCH] V11/feature/update to dotnet 7 (#12712) * Update projects to .NET 7 * Fix nullability errors * Fix up pipelines to run 7.0 * Update langversion to preview * Revert "Fix up pipelines to run 7.0" This reverts commit d0fa8d01b8126a4eaa59832a3814a567705419ae. * Fix up pipelines again, this time without indentation changes * Include preview versions * Versions not Version * Fix ModelTypeTests * Fix MemberPasswordHasherTests Microsoft wants to use SHA512 instead of SHA256, so our old hashes will return SuccessRehashNeeded now * Use dotnet cli instead of nuget restore * Update src/Umbraco.Web.UI/Umbraco.Web.UI.csproj * Update dependencies * Fix nullability issues * Fix unit test * Fix nullability in ChangingPasswordModel OldPassword can be null, if we're changing the password with password reset enabled. Additionally, we might as well use the new required keyword instead of supressing null. * Use required keyword instead of supressing null * Fix up pipelines again * fix up spelling-error * Use dotnet cli instead of nuget restore * Fix up another NuGet command * Use dotnet version 7 before building * Include preview versions * Remove condition * Use dotnet 7 before running powershell script * Update templates to .net 7 * Download version 7 before running linux container * Move use dotnet 7 even earlier in E2E process * Remove dotnet 7 * Reintroduce .NET 7 task * Update linux docker container and remove dotnet 7 from yml * Fix up dockerfile with ARG * Fix up docker file with nightly builds of dotnet 7 * Reintroduce dotnet 7 so windows can use it * Use aspnet 7 in docker Co-authored-by: Nikolaj Co-authored-by: Zeegaan --- build/azure-pipelines.yml | 113 ++++++++++-------- src/Directory.Build.props | 2 +- src/JsonSchema/JsonSchema.csproj | 2 +- .../Umbraco.Cms.Persistence.SqlServer.csproj | 2 +- .../Umbraco.Cms.Persistence.Sqlite.csproj | 3 +- .../Umbraco.Cms.StaticAssets.csproj | 2 +- src/Umbraco.Cms/Umbraco.Cms.csproj | 2 +- .../ConfigureConnectionStrings.cs | 7 +- .../Validation/ContentSettingsValidator.cs | 2 +- .../Validation/GlobalSettingsValidator.cs | 2 +- .../HealthChecksSettingsValidator.cs | 2 +- .../RequestHandlerSettingsValidator.cs | 2 +- .../Validation/UnattendedSettingsValidator.cs | 4 +- .../RequestHandlerSettingsExtension.cs | 25 +++- .../Hosting/IHostingEnvironment.cs | 2 +- .../Models/ChangingPasswordModel.cs | 2 +- src/Umbraco.Core/Models/SendCodeViewModel.cs | 6 +- src/Umbraco.Core/Models/SetPasswordModel.cs | 6 +- src/Umbraco.Core/Models/UnLinkLoginModel.cs | 6 +- src/Umbraco.Core/Umbraco.Core.csproj | 13 +- src/Umbraco.Core/Web/IRequestAccessor.cs | 4 +- .../ConfigureIndexOptions.cs | 2 +- .../Umbraco.Examine.Lucene.csproj | 2 +- .../Configuration/JsonConfigManipulator.cs | 4 +- .../BackOfficeClaimsPrincipalFactory.cs | 4 +- .../Security/BackOfficeErrorDescriber.cs | 4 +- .../Security/BackOfficeUserStore.cs | 77 ++++++------ .../Security/ClaimsIdentityExtensions.cs | 4 +- .../Security/IUmbracoUserManager.cs | 28 ++--- .../Security/MemberRoleStore.cs | 47 ++++---- .../Security/MemberUserStore.cs | 107 ++++++++--------- .../Security/NoOpLookupNormalizer.cs | 4 +- .../Security/UmbracoErrorDescriberBase.cs | 4 +- .../Security/UmbracoIdentityRole.cs | 14 +-- .../Security/UmbracoIdentityUser.cs | 6 +- .../Security/UmbracoUserManager.cs | 14 +-- .../Security/UmbracoUserStore.cs | 14 +-- .../Umbraco.Infrastructure.csproj | 10 +- .../Umbraco.PublishedCache.NuCache.csproj | 2 +- .../Controllers/AuthenticationController.cs | 35 ++++-- .../Controllers/BackOfficeController.cs | 7 +- .../Controllers/BackOfficeServerVariables.cs | 2 +- .../Controllers/CurrentUserController.cs | 23 +++- .../Controllers/MemberController.cs | 10 +- .../Controllers/TwoFactorLoginController.cs | 6 +- .../Controllers/UsersController.cs | 16 ++- .../UmbracoBuilder.LocalizedText.cs | 4 +- ...CreateUnattendedUserNotificationHandler.cs | 2 +- .../Install/InstallApiController.cs | 8 +- ...igureGlobalOptionsForKeepAliveMiddlware.cs | 4 +- .../BackOfficeAuthenticationBuilder.cs | 4 +- .../Security/BackOfficeSessionIdValidator.cs | 7 +- .../Security/BackOfficeSignInManager.cs | 6 +- .../ConfigureBackOfficeCookieOptions.cs | 2 +- .../Security/PasswordChanger.cs | 4 +- .../Services/IconService.cs | 2 +- .../Umbraco.Web.BackOffice.csproj | 2 +- .../AspNetCoreHostingEnvironment.cs | 2 +- .../AspNetCore/AspNetCoreRequestAccessor.cs | 4 +- .../AspNetCore/OptionsMonitorAdapter.cs | 2 +- .../Extensions/FormCollectionExtensions.cs | 2 +- .../Extensions/HttpContextExtensions.cs | 4 +- .../Routing/RoutableDocumentFilter.cs | 2 +- .../Security/BackOfficeUserManager.cs | 4 +- .../Security/ConfigureMemberCookieOptions.cs | 2 +- .../Security/ConfigureSecurityStampOptions.cs | 11 +- .../Security/IBackOfficeSignInManager.cs | 4 +- .../Security/IMemberSignInManager.cs | 4 +- .../Security/MemberManager.cs | 6 +- .../Security/MemberSignInManager.cs | 8 +- .../Security/PublicAccessChecker.cs | 2 +- .../Security/UmbracoSignInManager.cs | 10 +- .../Umbraco.Web.Common.csproj | 6 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 25 +++- .../Controllers/UmbExternalLoginController.cs | 19 ++- .../Controllers/UmbLoginController.cs | 2 +- .../Models/ProfileModelBuilder.cs | 2 +- .../Security/MemberAuthenticationBuilder.cs | 4 +- .../Umbraco.Web.Website.csproj | 2 +- templates/Umbraco.Templates.csproj | 2 +- .../UmbracoPackage/UmbracoPackage.csproj | 2 +- .../UmbracoProject/UmbracoProject.csproj | 2 +- .../Umbraco.TestData/Umbraco.TestData.csproj | 2 +- .../misc/umbraco-linux.docker | 4 +- .../Umbraco.Tests.Benchmarks.csproj | 4 +- .../Umbraco.Tests.Common.csproj | 2 +- .../Umbraco.Tests.Integration.csproj | 4 +- .../Umbraco.Core/Published/ModelTypeTests.cs | 2 +- .../Security/MemberPasswordHasherTests.cs | 7 +- .../Umbraco.Tests.UnitTests.csproj | 6 +- 90 files changed, 496 insertions(+), 368 deletions(-) diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index ae8ca25f52..328c8dfef5 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -64,6 +64,11 @@ stages: gulpFile: src/Umbraco.Web.UI.Client/gulpfile.js targets: coreBuild workingDirectory: src/Umbraco.Web.UI.Client + - task: UseDotNet@2 + displayName: Use .Net 7.x + inputs: + version: 7.x + includePreviewVersions: true - task: DotNetCoreCLI@2 displayName: Run dotnet build inputs: @@ -130,41 +135,41 @@ stages: - task: PowerShell@2 displayName: Install DocFX inputs: - targetType: inline - script: | - choco install docfx --version=2.59.2 -y - if ($lastexitcode -ne 0){ - throw ("Error installing DocFX") - } + targetType: inline + script: | + choco install docfx --version=2.59.2 -y + if ($lastexitcode -ne 0){ + throw ("Error installing DocFX") + } - task: PowerShell@2 displayName: Generate metadata inputs: - targetType: inline - script: | - docfx metadata "$(Build.SourcesDirectory)/build/csharp-docs/docfx.json" - if ($lastexitcode -ne 0){ - throw ("Error generating metadata.") - } + targetType: inline + script: | + docfx metadata "$(Build.SourcesDirectory)/build/csharp-docs/docfx.json" + if ($lastexitcode -ne 0){ + throw ("Error generating metadata.") + } - task: PowerShell@2 displayName: Generate documentation inputs: - targetType: inline - script: | - docfx build "$(Build.SourcesDirectory)/build/csharp-docs/docfx.json" - if ($lastexitcode -ne 0){ - throw ("Error generating documentation.") - } + targetType: inline + script: | + docfx build "$(Build.SourcesDirectory)/build/csharp-docs/docfx.json" + if ($lastexitcode -ne 0){ + throw ("Error generating documentation.") + } - task: ArchiveFiles@2 displayName: Archive C# Docs inputs: - rootFolderOrFile: $(Build.SourcesDirectory)/build/csharp-docs/_site - includeRootFolder: false - archiveFile: $(Build.ArtifactStagingDirectory)/csharp-docs.zip + rootFolderOrFile: $(Build.SourcesDirectory)/build/csharp-docs/_site + includeRootFolder: false + archiveFile: $(Build.ArtifactStagingDirectory)/csharp-docs.zip - task: PublishPipelineArtifact@1 displayName: Publish C# Docs inputs: - targetPath: $(Build.ArtifactStagingDirectory)/csharp-docs.zip - artifact: csharp-docs + targetPath: $(Build.ArtifactStagingDirectory)/csharp-docs.zip + artifact: csharp-docs # js API Reference - job: @@ -192,14 +197,14 @@ stages: - task: ArchiveFiles@2 displayName: Archive js Docs inputs: - rootFolderOrFile: $(Build.SourcesDirectory)/src/Umbraco.Web.UI.Docs/api - includeRootFolder: false - archiveFile: $(Build.ArtifactStagingDirectory)/ui-docs.zip + rootFolderOrFile: $(Build.SourcesDirectory)/src/Umbraco.Web.UI.Docs/api + includeRootFolder: false + archiveFile: $(Build.ArtifactStagingDirectory)/ui-docs.zip - task: PublishPipelineArtifact@1 displayName: Publish js Docs inputs: - targetPath: $(Build.ArtifactStagingDirectory)/ui-docs.zip - artifact: ui-docs + targetPath: $(Build.ArtifactStagingDirectory)/ui-docs.zip + artifact: ui-docs ############################################### ## Test @@ -228,10 +233,10 @@ stages: artifact: build_output path: $(Build.SourcesDirectory) - task: UseDotNet@2 - condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin')) # net6 already on the other images - displayName: Use net6 + displayName: Use net7 inputs: - version: 6.x + version: 7.x + includePreviewVersions: true - task: DotNetCoreCLI@2 displayName: Run dotnet test inputs: @@ -264,10 +269,10 @@ stages: artifact: build_output path: $(Build.SourcesDirectory) - task: UseDotNet@2 - displayName: Use net6 - condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin')) # net6 already on the other images + displayName: Use net7 inputs: - version: 6.x + version: 7.x + includePreviewVersions: true - task: DotNetCoreCLI@2 displayName: Run dotnet test inputs: @@ -305,7 +310,7 @@ stages: - powershell: sqllocaldb start mssqllocaldb displayName: Start localdb (Windows only) condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - - powershell: docker run --name mssql -d -p 1433:1433 -e ACCEPT_EULA=Y -e SA_PASSWORD=$(SA_PASSWORD) -e MSSQL_PID=Developer mcr.microsoft.com/mssql/server:2019-latest + - powershell: docker run --name mssql -d -p 1433:1433 -e ACCEPT_EULA=Y -e SA_PASSWORD=$(SA_PASSWORD) -e MSSQL_PID=Developer mcr.microsoft.com/mssql/server:2019-latest displayName: Start SQL Server (Linux only) condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) - task: DotNetCoreCLI@2 @@ -327,7 +332,7 @@ stages: displayName: E2E Tests dependsOn: Build jobs: - # E2E Tests + # E2E Tests - job: displayName: E2E Tests variables: @@ -358,7 +363,7 @@ stages: Windows: vmImage: 'windows-latest' pool: - vmImage: $(vmImage) + vmImage: $(vmImage) steps: - task: DownloadPipelineArtifact@2 displayName: Download nupkg @@ -397,7 +402,12 @@ stages: - powershell: Invoke-Sqlcmd -Query "CREATE DATABASE $env:UmbracoDatabaseName" -ServerInstance $env:UmbracoDatabaseServer displayName: Create database (Windows only) condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - # Linux containers smooth + - task: UseDotNet@2 + displayName: Use .Net 7.x + inputs: + version: 7.x + includePreviewVersions: true + # Linux containers smooth - task: PowerShell@2 condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) displayName: Build & run container (Linux only) @@ -482,14 +492,17 @@ stages: inputs: artifact: nupkg path: $(Build.ArtifactStagingDirectory)/nupkg - - task: NuGetCommand@2 - displayName: Nuget push + - task: DotNetCoreCLI@2 + displayName: dotnet restore inputs: - command: 'push' - packagesToPush: $(Build.ArtifactStagingDirectory)/**/*.nupkg - nuGetFeedType: 'external' - publishFeedCredentials: 'MyGet - Pre-releases' - + command: restore + projects: '**/umbraco.sln' + # TODO: Use NuGetCommand instead of DotNetCoreCLI + # - task: NuGetCommand@2 + # displayName: Restore NuGet Packages + # inputs: + # restoreSolution: 'umbraco.sln' + # feedsToUse: config - stage: Deploy_NuGet displayName: NuGet release dependsOn: @@ -506,13 +519,11 @@ stages: inputs: artifact: nupkg path: $(Build.ArtifactStagingDirectory)/nupkg - - task: NuGetCommand@2 - displayName: Nuget push + - task: DotNetCoreCLI@2 + displayName: dotnet restore inputs: - command: 'push' - packagesToPush: $(Build.ArtifactStagingDirectory)/**/*.nupkg - nuGetFeedType: 'external' - publishFeedCredentials: 'NuGet - Umbraco.*' + command: restore + projects: '**/umbraco.sln' - stage: Upload_API_Docs pool: diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 3505baf38e..657774fcc0 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -7,7 +7,7 @@ 10.0.0 10.0.0-rc1 10.0.0 - 10.0 + preview en-US Umbraco CMS Copyright © Umbraco 2021 diff --git a/src/JsonSchema/JsonSchema.csproj b/src/JsonSchema/JsonSchema.csproj index ea0ce9b7c3..bd4f04071b 100644 --- a/src/JsonSchema/JsonSchema.csproj +++ b/src/JsonSchema/JsonSchema.csproj @@ -1,7 +1,7 @@ Exe - net6.0 + net7.0 true false false diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj b/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj index e206cd8653..e78c210ae2 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj +++ b/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 Umbraco.Cms.Persistence.SqlServer Umbraco.Cms.Persistence.SqlServer Adds support for SQL Server to Umbraco CMS. diff --git a/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj b/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj index 5aa062df17..943757b353 100644 --- a/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj +++ b/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 Umbraco.Cms.Persistence.Sqlite Umbraco.Cms.Persistence.Sqlite Adds support for SQLite to Umbraco CMS. @@ -12,6 +12,7 @@ + diff --git a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj index 1fbbd8c42f..89cb8a44aa 100644 --- a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj +++ b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 true Umbraco.Cms.StaticAssets Contains the static assets that is required to run Umbraco CMS. diff --git a/src/Umbraco.Cms/Umbraco.Cms.csproj b/src/Umbraco.Cms/Umbraco.Cms.csproj index 23e8febd18..815be058eb 100644 --- a/src/Umbraco.Cms/Umbraco.Cms.csproj +++ b/src/Umbraco.Cms/Umbraco.Cms.csproj @@ -1,6 +1,6 @@ - net6.0 + net7.0 false Umbraco.Cms Umbraco.Cms diff --git a/src/Umbraco.Core/Configuration/ConfigureConnectionStrings.cs b/src/Umbraco.Core/Configuration/ConfigureConnectionStrings.cs index cd256e1b45..69ef69239e 100644 --- a/src/Umbraco.Core/Configuration/ConfigureConnectionStrings.cs +++ b/src/Umbraco.Core/Configuration/ConfigureConnectionStrings.cs @@ -23,8 +23,13 @@ public class ConfigureConnectionStrings : IConfigureNamedOptions Configure(Options.DefaultName, options); /// - public void Configure(string name, ConnectionStrings options) + public void Configure(string? name, ConnectionStrings options) { + if (name is null) + { + throw new InvalidOperationException("The name of the option instance is required."); + } + // Default to using UmbracoConnectionName if (name == Options.DefaultName) { diff --git a/src/Umbraco.Core/Configuration/Models/Validation/ContentSettingsValidator.cs b/src/Umbraco.Core/Configuration/Models/Validation/ContentSettingsValidator.cs index 0798014600..5062dc87e4 100644 --- a/src/Umbraco.Core/Configuration/Models/Validation/ContentSettingsValidator.cs +++ b/src/Umbraco.Core/Configuration/Models/Validation/ContentSettingsValidator.cs @@ -11,7 +11,7 @@ namespace Umbraco.Cms.Core.Configuration.Models.Validation; public class ContentSettingsValidator : ConfigurationValidatorBase, IValidateOptions { /// - public ValidateOptionsResult Validate(string name, ContentSettings options) + public ValidateOptionsResult Validate(string? name, ContentSettings options) { if (!ValidateError404Collection(options.Error404Collection, out var message)) { diff --git a/src/Umbraco.Core/Configuration/Models/Validation/GlobalSettingsValidator.cs b/src/Umbraco.Core/Configuration/Models/Validation/GlobalSettingsValidator.cs index 32ad130c33..f78ce306dd 100644 --- a/src/Umbraco.Core/Configuration/Models/Validation/GlobalSettingsValidator.cs +++ b/src/Umbraco.Core/Configuration/Models/Validation/GlobalSettingsValidator.cs @@ -12,7 +12,7 @@ public class GlobalSettingsValidator : ConfigurationValidatorBase, IValidateOptions { /// - public ValidateOptionsResult Validate(string name, GlobalSettings options) + public ValidateOptionsResult Validate(string? name, GlobalSettings options) { if (!ValidateSmtpSetting(options.Smtp, out var message)) { diff --git a/src/Umbraco.Core/Configuration/Models/Validation/HealthChecksSettingsValidator.cs b/src/Umbraco.Core/Configuration/Models/Validation/HealthChecksSettingsValidator.cs index ac0e1651ea..2b55afdcb0 100644 --- a/src/Umbraco.Core/Configuration/Models/Validation/HealthChecksSettingsValidator.cs +++ b/src/Umbraco.Core/Configuration/Models/Validation/HealthChecksSettingsValidator.cs @@ -19,7 +19,7 @@ public class HealthChecksSettingsValidator : ConfigurationValidatorBase, IValida public HealthChecksSettingsValidator(ICronTabParser cronTabParser) => _cronTabParser = cronTabParser; /// - public ValidateOptionsResult Validate(string name, HealthChecksSettings options) + public ValidateOptionsResult Validate(string? name, HealthChecksSettings options) { if (!ValidateNotificationFirstRunTime(options.Notification.FirstRunTime, out var message)) { diff --git a/src/Umbraco.Core/Configuration/Models/Validation/RequestHandlerSettingsValidator.cs b/src/Umbraco.Core/Configuration/Models/Validation/RequestHandlerSettingsValidator.cs index 4a1872cf30..8515fc3cc4 100644 --- a/src/Umbraco.Core/Configuration/Models/Validation/RequestHandlerSettingsValidator.cs +++ b/src/Umbraco.Core/Configuration/Models/Validation/RequestHandlerSettingsValidator.cs @@ -11,7 +11,7 @@ namespace Umbraco.Cms.Core.Configuration.Models.Validation; public class RequestHandlerSettingsValidator : ConfigurationValidatorBase, IValidateOptions { /// - public ValidateOptionsResult Validate(string name, RequestHandlerSettings options) + public ValidateOptionsResult Validate(string? name, RequestHandlerSettings options) { if (!ValidateConvertUrlsToAscii(options.ConvertUrlsToAscii, out var message)) { diff --git a/src/Umbraco.Core/Configuration/Models/Validation/UnattendedSettingsValidator.cs b/src/Umbraco.Core/Configuration/Models/Validation/UnattendedSettingsValidator.cs index e262de76e7..473224553a 100644 --- a/src/Umbraco.Core/Configuration/Models/Validation/UnattendedSettingsValidator.cs +++ b/src/Umbraco.Core/Configuration/Models/Validation/UnattendedSettingsValidator.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using Microsoft.Extensions.Options; @@ -12,7 +12,7 @@ public class UnattendedSettingsValidator : IValidateOptions { /// - public ValidateOptionsResult Validate(string name, UnattendedSettings options) + public ValidateOptionsResult Validate(string? name, UnattendedSettings options) { if (options.InstallUnattended) { diff --git a/src/Umbraco.Core/Extensions/RequestHandlerSettingsExtension.cs b/src/Umbraco.Core/Extensions/RequestHandlerSettingsExtension.cs index a083f89cc6..3452059e9b 100644 --- a/src/Umbraco.Core/Extensions/RequestHandlerSettingsExtension.cs +++ b/src/Umbraco.Core/Extensions/RequestHandlerSettingsExtension.cs @@ -28,9 +28,28 @@ public static class RequestHandlerSettingsExtension return RequestHandlerSettings.DefaultCharCollection; } - return MergeUnique( - requestHandlerSettings.UserDefinedCharCollection, - RequestHandlerSettings.DefaultCharCollection); + return MergeUnique(requestHandlerSettings.UserDefinedCharCollection, RequestHandlerSettings.DefaultCharCollection); + } + + private static IEnumerable GetReplacements(IConfiguration configuration, string key) + { + var replacements = new List(); + IEnumerable config = configuration.GetSection(key).GetChildren(); + + foreach (IConfigurationSection section in config) + { + var @char = section.GetValue(nameof(CharItem.Char)); + var replacement = section.GetValue(nameof(CharItem.Replacement)); + + if (@char is null || replacement is null) + { + continue; + } + + replacements.Add(new CharItem { Char = @char, Replacement = replacement }); + } + + return replacements; } /// diff --git a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs index b8960048f6..1dfa72039c 100644 --- a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs +++ b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs @@ -2,7 +2,7 @@ namespace Umbraco.Cms.Core.Hosting; public interface IHostingEnvironment { - string SiteName { get; } + string? SiteName { get; } /// /// The unique application ID for this Umbraco website. diff --git a/src/Umbraco.Core/Models/ChangingPasswordModel.cs b/src/Umbraco.Core/Models/ChangingPasswordModel.cs index 946bcde9ab..ecba35f137 100644 --- a/src/Umbraco.Core/Models/ChangingPasswordModel.cs +++ b/src/Umbraco.Core/Models/ChangingPasswordModel.cs @@ -11,7 +11,7 @@ public class ChangingPasswordModel /// The password value /// [DataMember(Name = "newPassword")] - public string? NewPassword { get; set; } + public required string NewPassword { get; set; } /// /// The old password - used to change a password when: EnablePasswordRetrieval = false diff --git a/src/Umbraco.Core/Models/SendCodeViewModel.cs b/src/Umbraco.Core/Models/SendCodeViewModel.cs index c73fd73eb3..29d318f8ff 100644 --- a/src/Umbraco.Core/Models/SendCodeViewModel.cs +++ b/src/Umbraco.Core/Models/SendCodeViewModel.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; namespace Umbraco.Cms.Core.Models; @@ -11,11 +11,11 @@ public class Verify2FACodeModel { [Required] [DataMember(Name = "code", IsRequired = true)] - public string? Code { get; set; } + public required string Code { get; set; } [Required] [DataMember(Name = "provider", IsRequired = true)] - public string? Provider { get; set; } + public required string Provider { get; set; } /// /// Flag indicating whether the sign-in cookie should persist after the browser is closed. diff --git a/src/Umbraco.Core/Models/SetPasswordModel.cs b/src/Umbraco.Core/Models/SetPasswordModel.cs index 57d1abc38f..58803c101d 100644 --- a/src/Umbraco.Core/Models/SetPasswordModel.cs +++ b/src/Umbraco.Core/Models/SetPasswordModel.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; namespace Umbraco.Cms.Core.Models; @@ -12,9 +12,9 @@ public class SetPasswordModel [Required] [DataMember(Name = "password", IsRequired = true)] - public string? Password { get; set; } + public required string Password { get; set; } [Required] [DataMember(Name = "resetCode", IsRequired = true)] - public string? ResetCode { get; set; } + public required string ResetCode { get; set; } } diff --git a/src/Umbraco.Core/Models/UnLinkLoginModel.cs b/src/Umbraco.Core/Models/UnLinkLoginModel.cs index c121230810..ba4d881b73 100644 --- a/src/Umbraco.Core/Models/UnLinkLoginModel.cs +++ b/src/Umbraco.Core/Models/UnLinkLoginModel.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; namespace Umbraco.Cms.Core.Models; @@ -7,9 +7,9 @@ public class UnLinkLoginModel { [Required] [DataMember(Name = "loginProvider", IsRequired = true)] - public string? LoginProvider { get; set; } + public required string LoginProvider { get; set; } [Required] [DataMember(Name = "providerKey", IsRequired = true)] - public string? ProviderKey { get; set; } + public required string ProviderKey { get; set; } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 01183739d5..8ee0b780b0 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 Umbraco.Cms.Core Umbraco CMS Umbraco.Cms.Core @@ -15,6 +15,15 @@ + + + + + + + + + @@ -30,7 +39,7 @@ - + all diff --git a/src/Umbraco.Core/Web/IRequestAccessor.cs b/src/Umbraco.Core/Web/IRequestAccessor.cs index a72ec5bc72..57940b3c1b 100644 --- a/src/Umbraco.Core/Web/IRequestAccessor.cs +++ b/src/Umbraco.Core/Web/IRequestAccessor.cs @@ -5,12 +5,12 @@ public interface IRequestAccessor /// /// Returns the request/form/querystring value for the given name /// - string GetRequestValue(string name); + string? GetRequestValue(string name); /// /// Returns the query string value for the given name /// - string GetQueryStringValue(string name); + string? GetQueryStringValue(string name); /// /// Returns the current request uri diff --git a/src/Umbraco.Examine.Lucene/DependencyInjection/ConfigureIndexOptions.cs b/src/Umbraco.Examine.Lucene/DependencyInjection/ConfigureIndexOptions.cs index e6306ab444..f7c3cf9a3e 100644 --- a/src/Umbraco.Examine.Lucene/DependencyInjection/ConfigureIndexOptions.cs +++ b/src/Umbraco.Examine.Lucene/DependencyInjection/ConfigureIndexOptions.cs @@ -25,7 +25,7 @@ public sealed class ConfigureIndexOptions : IConfigureNamedOptions - net6.0 + net7.0 Umbraco.Cms.Infrastructure.Examine Umbraco CMS Umbraco.Examine.Lucene diff --git a/src/Umbraco.Infrastructure/Configuration/JsonConfigManipulator.cs b/src/Umbraco.Infrastructure/Configuration/JsonConfigManipulator.cs index 9481eb9958..1e17b959b6 100644 --- a/src/Umbraco.Infrastructure/Configuration/JsonConfigManipulator.cs +++ b/src/Umbraco.Infrastructure/Configuration/JsonConfigManipulator.cs @@ -229,7 +229,7 @@ namespace Umbraco.Cms.Core.Configuration { if (provider.Source.FileProvider is PhysicalFileProvider physicalFileProvider) { - var jsonFilePath = Path.Combine(physicalFileProvider.Root, provider.Source.Path); + var jsonFilePath = Path.Combine(physicalFileProvider.Root, provider.Source.Path!); try { @@ -264,7 +264,7 @@ namespace Umbraco.Cms.Core.Configuration return null; } - var jsonFilePath = Path.Combine(physicalFileProvider.Root, provider.Source.Path); + var jsonFilePath = Path.Combine(physicalFileProvider.Root, provider.Source.Path!); try { diff --git a/src/Umbraco.Infrastructure/Security/BackOfficeClaimsPrincipalFactory.cs b/src/Umbraco.Infrastructure/Security/BackOfficeClaimsPrincipalFactory.cs index 9555482bbf..a6589166b2 100644 --- a/src/Umbraco.Infrastructure/Security/BackOfficeClaimsPrincipalFactory.cs +++ b/src/Umbraco.Infrastructure/Security/BackOfficeClaimsPrincipalFactory.cs @@ -46,12 +46,12 @@ public class BackOfficeClaimsPrincipalFactory : UserClaimsPrincipalFactory x.RoleId).ToArray()); diff --git a/src/Umbraco.Infrastructure/Security/BackOfficeErrorDescriber.cs b/src/Umbraco.Infrastructure/Security/BackOfficeErrorDescriber.cs index 2b9daab29f..b99eddba5d 100644 --- a/src/Umbraco.Infrastructure/Security/BackOfficeErrorDescriber.cs +++ b/src/Umbraco.Infrastructure/Security/BackOfficeErrorDescriber.cs @@ -20,7 +20,7 @@ public class BackOfficeErrorDescriber : UmbracoErrorDescriberBase Description = _textService.Localize("validation", "duplicateUserGroupName", new[] { role }), }; - public override IdentityError InvalidRoleName(string role) => new() + public override IdentityError InvalidRoleName(string? role) => new() { Code = nameof(InvalidRoleName), Description = _textService.Localize("validation", "invalidUserGroupName"), @@ -70,7 +70,7 @@ public class MembersErrorDescriber : UmbracoErrorDescriberBase Description = _textService.Localize("validation", "duplicateMemberGroupName", new[] { role }), }; - public override IdentityError InvalidRoleName(string role) => new() + public override IdentityError InvalidRoleName(string? role) => new() { Code = nameof(InvalidRoleName), Description = _textService.Localize("validation", "invalidMemberGroupName"), diff --git a/src/Umbraco.Infrastructure/Security/BackOfficeUserStore.cs b/src/Umbraco.Infrastructure/Security/BackOfficeUserStore.cs index 729373faba..1908a3fbfc 100644 --- a/src/Umbraco.Infrastructure/Security/BackOfficeUserStore.cs +++ b/src/Umbraco.Infrastructure/Security/BackOfficeUserStore.cs @@ -117,12 +117,17 @@ public class BackOfficeUserStore : UmbracoUserStore(); - var emptyPasswordValue = Constants.Security.EmptyPasswordPrefix + + if (user.Email is null || user.UserName is null) + { + throw new InvalidOperationException("Email and UserName is required."); + } + + // the password must be 'something' it could be empty if authenticating + // with an external provider so we'll just generate one and prefix it, the + // prefix will help us determine if the password hasn't actually been specified yet. + // this will hash the guid with a salt so should be nicely random + var aspHasher = new PasswordHasher(); + var emptyPasswordValue = Constants.Security.EmptyPasswordPrefix + aspHasher.HashPassword(user, Guid.NewGuid().ToString("N")); var userEntity = new User(_globalSettings, user.Name, user.Email, user.UserName, emptyPasswordValue) @@ -255,16 +260,14 @@ public class BackOfficeUserStore : UmbracoUserStore - public override Task FindByNameAsync( - string userName, - CancellationToken cancellationToken = default) + public override Task FindByNameAsync(string userName, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); IUser? user = _userService.GetByUsername(userName); if (user == null) { - return Task.FromResult((BackOfficeIdentityUser)null!); + return Task.FromResult(null); } BackOfficeIdentityUser? result = AssignLoginsCallback(_mapper.Map(user)); @@ -273,7 +276,7 @@ public class BackOfficeUserStore : UmbracoUserStore - protected override Task FindUserAsync(string userId, CancellationToken cancellationToken) + protected override Task FindUserAsync(string userId, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); @@ -288,7 +291,7 @@ public class BackOfficeUserStore : UmbracoUserStore - public override Task FindByEmailAsync( + public override Task FindByEmailAsync( string email, CancellationToken cancellationToken = default) { @@ -299,11 +302,11 @@ public class BackOfficeUserStore : UmbracoUserStore(user); - return Task.FromResult(AssignLoginsCallback(result))!; + return Task.FromResult(AssignLoginsCallback(result)); } /// - public override async Task SetPasswordHashAsync(BackOfficeIdentityUser user, string passwordHash, CancellationToken cancellationToken = default) + public override async Task SetPasswordHashAsync(BackOfficeIdentityUser user, string? passwordHash, CancellationToken cancellationToken = default) { await base.SetPasswordHashAsync(user, passwordHash, cancellationToken); @@ -405,7 +408,7 @@ public class BackOfficeUserStore : UmbracoUserStore /// - public override Task SetTokenAsync(BackOfficeIdentityUser user, string loginProvider, string name, string value, CancellationToken cancellationToken) + public override Task SetTokenAsync(BackOfficeIdentityUser user, string loginProvider, string name, string? value, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); @@ -437,15 +440,15 @@ public class BackOfficeUserStore : UmbracoUserStore - protected override async Task> FindUserLoginAsync(string userId, string loginProvider, string providerKey, CancellationToken cancellationToken) + protected override async Task?> FindUserLoginAsync(string userId, string loginProvider, string providerKey, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); - BackOfficeIdentityUser user = await FindUserAsync(userId, cancellationToken); - if (user is null || user.Id is null) + BackOfficeIdentityUser? user = await FindUserAsync(userId, cancellationToken); + if (user?.Id is null) { - return null!; + return null; } IList logins = await GetLoginsAsync(user, cancellationToken); @@ -453,7 +456,7 @@ public class BackOfficeUserStore : UmbracoUserStore x.ProviderKey == providerKey && x.LoginProvider == loginProvider); if (found == null) { - return null!; + return null; } return new IdentityUserLogin @@ -466,7 +469,7 @@ public class BackOfficeUserStore : UmbracoUserStore - protected override Task> FindUserLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken) + protected override Task?> FindUserLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); @@ -474,11 +477,11 @@ public class BackOfficeUserStore : UmbracoUserStore)null!); + return Task.FromResult?>(null); } IIdentityUserLogin found = logins[0]; - return Task.FromResult(new IdentityUserLogin + return Task.FromResult?>(new IdentityUserLogin { LoginProvider = found.LoginProvider, ProviderKey = found.ProviderKey, @@ -488,30 +491,33 @@ public class BackOfficeUserStore : UmbracoUserStore - protected override Task> FindRoleAsync( + protected override Task?> FindRoleAsync( string normalizedRoleName, CancellationToken cancellationToken) { IUserGroup? group = _userService.GetUserGroupByAlias(normalizedRoleName); - if (group == null) + if (group?.Name is null) { - return Task.FromResult((IdentityRole)null!); + return Task.FromResult?>(null); } - return Task.FromResult(new IdentityRole(group.Name) { Id = group.Alias }); + return Task.FromResult?>(new IdentityRole(group.Name) + { + Id = group.Alias, + }); } /// - protected override async Task> FindUserRoleAsync(string userId, string roleId, CancellationToken cancellationToken) + protected override async Task?> FindUserRoleAsync(string userId, string roleId, CancellationToken cancellationToken) { - BackOfficeIdentityUser user = await FindUserAsync(userId, cancellationToken); + BackOfficeIdentityUser? user = await FindUserAsync(userId, cancellationToken); if (user == null) { return null!; } IdentityUserRole? found = user.Roles.FirstOrDefault(x => x.RoleId.InvariantEquals(roleId)); - return found!; + return found; } private BackOfficeIdentityUser? AssignLoginsCallback(BackOfficeIdentityUser? user) @@ -583,7 +589,7 @@ public class BackOfficeUserStore : UmbracoUserStore - /// Overridden to support Umbraco's own data storage requirements + /// Overridden to support Umbraco's own data storage requirements /// /// - /// The base class's implementation of this calls into FindTokenAsync, RemoveUserTokenAsync and AddUserTokenAsync, both - /// methods will only work with ORMs that are change - /// tracking ORMs like EFCore. + /// The base class's implementation of this calls into FindTokenAsync, RemoveUserTokenAsync and AddUserTokenAsync, both methods will only work with ORMs that are change + /// tracking ORMs like EFCore. /// /// public override Task RemoveTokenAsync(BackOfficeIdentityUser user, string loginProvider, string name, CancellationToken cancellationToken) diff --git a/src/Umbraco.Infrastructure/Security/ClaimsIdentityExtensions.cs b/src/Umbraco.Infrastructure/Security/ClaimsIdentityExtensions.cs index 05ab03b784..c914930e9e 100644 --- a/src/Umbraco.Infrastructure/Security/ClaimsIdentityExtensions.cs +++ b/src/Umbraco.Infrastructure/Security/ClaimsIdentityExtensions.cs @@ -38,9 +38,9 @@ public static class MergeClaimsIdentityExtensions { foreach (IdentityUserClaim claim in source.Claims .Where(claim => !_ignoredClaims.Contains(claim.ClaimType)) - .Where(claim => !destination.HasClaim(claim.ClaimType, claim.ClaimValue))) + .Where(claim => !destination.HasClaim(claim.ClaimType!, claim.ClaimValue!))) { - destination.AddClaim(new Claim(claim.ClaimType, claim.ClaimValue)); + destination.AddClaim(new Claim(claim.ClaimType!, claim.ClaimValue!)); } } } diff --git a/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs b/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs index fe34812334..714db070bb 100644 --- a/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs +++ b/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs @@ -22,14 +22,14 @@ public interface IUmbracoUserManager : IDisposable /// /// The /// A representing the result of the asynchronous operation. - Task GetUserAsync(ClaimsPrincipal principal); + Task GetUserAsync(ClaimsPrincipal principal); /// /// Get the user id from the /// /// the /// Returns the user id from the - string GetUserId(ClaimsPrincipal principal); + string? GetUserId(ClaimsPrincipal principal); /// /// Gets the external logins for the user @@ -47,7 +47,7 @@ public interface IUmbracoUserManager : IDisposable /// Finds a user by the external login provider /// /// A representing the result of the asynchronous operation. - Task FindByLoginAsync(string loginProvider, string providerKey); + Task FindByLoginAsync(string loginProvider, string providerKey); /// /// Finds and returns a user, if any, who has the specified . @@ -57,7 +57,7 @@ public interface IUmbracoUserManager : IDisposable /// The that represents the asynchronous operation, containing the user matching the specified /// if it exists. /// - Task FindByIdAsync(string? userId); + Task FindByIdAsync(string userId); /// /// Generates a password reset token for the specified , using @@ -80,7 +80,7 @@ public interface IUmbracoUserManager : IDisposable /// 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 . @@ -91,7 +91,7 @@ public interface IUmbracoUserManager : IDisposable /// The that represents the asynchronous operation, containing the /// of the operation. /// - Task ConfirmEmailAsync(TUser user, string? token); + Task ConfirmEmailAsync(TUser user, string token); /// /// Gets the user, if any, associated with the normalized value of the specified email address. @@ -103,7 +103,7 @@ public interface IUmbracoUserManager : IDisposable /// The task object containing the results of the asynchronous lookup operation, the user, if any, associated with a /// normalized value of the specified email address. /// - Task FindByEmailAsync(string email); + Task FindByEmailAsync(string email); /// /// Resets the 's password to the specified after @@ -116,7 +116,7 @@ public interface IUmbracoUserManager : IDisposable /// The that represents the asynchronous operation, containing the /// of the operation. /// - Task ResetPasswordAsync(TUser user, string? token, string? newPassword); + Task ResetPasswordAsync(TUser user, string token, string newPassword); /// /// Override to check the user approval value as well as the user lock out date, by default this only checks the user's @@ -216,7 +216,7 @@ public interface IUmbracoUserManager : IDisposable /// 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 @@ -268,7 +268,7 @@ public interface IUmbracoUserManager : IDisposable /// The that represents the asynchronous operation, containing the /// of the operation. /// - Task CreateAsync(TUser user, string? password); + Task CreateAsync(TUser user, string password); /// /// Generate a password for a user based on the current password validator @@ -302,7 +302,7 @@ public interface IUmbracoUserManager : IDisposable /// The that represents the asynchronous operation, containing the user matching the specified /// if it exists. /// - Task FindByNameAsync(string userName); + Task FindByNameAsync(string userName); /// /// Increments the access failed count for the user as an asynchronous operation. @@ -373,7 +373,7 @@ public interface IUmbracoUserManager : IDisposable /// The System.Threading.Tasks.Task that represents the asynchronous operation, containing the /// Microsoft.AspNetCore.Identity.IdentityResult of the operation. /// - Task RemoveLoginAsync(TUser user, string? loginProvider, string? providerKey); + Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey); /// /// Resets the access failed count for the user @@ -395,7 +395,7 @@ public interface IUmbracoUserManager : IDisposable /// The task object containing the results of the asynchronous operation, the email address for the specified /// user. /// - Task GetEmailAsync(TUser user); + Task GetEmailAsync(TUser user); /// /// Gets the telephone number, if any, for the specified user. @@ -409,7 +409,7 @@ public interface IUmbracoUserManager : IDisposable /// A user can only support a phone number if the BackOfficeUserStore is replaced with another that implements /// IUserPhoneNumberStore /// - Task GetPhoneNumberAsync(TUser user); + Task GetPhoneNumberAsync(TUser user); /// /// Validates that a user's credentials are correct without actually logging them in. diff --git a/src/Umbraco.Infrastructure/Security/MemberRoleStore.cs b/src/Umbraco.Infrastructure/Security/MemberRoleStore.cs index eeeed4e4ec..b355bbcb18 100644 --- a/src/Umbraco.Infrastructure/Security/MemberRoleStore.cs +++ b/src/Umbraco.Infrastructure/Security/MemberRoleStore.cs @@ -131,7 +131,7 @@ public class MemberRoleStore : IQueryableRoleStore } /// - public Task GetRoleNameAsync(UmbracoIdentityRole role, CancellationToken cancellationToken = default) + public Task GetRoleNameAsync(UmbracoIdentityRole role, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); @@ -141,11 +141,11 @@ public class MemberRoleStore : IQueryableRoleStore throw new ArgumentNullException(nameof(role)); } - return Task.FromResult(role.Name)!; + return Task.FromResult(role.Name); } /// - public Task SetRoleNameAsync(UmbracoIdentityRole role, string roleName, + public Task SetRoleNameAsync(UmbracoIdentityRole role, string? roleName, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); @@ -155,23 +155,24 @@ public class MemberRoleStore : IQueryableRoleStore throw new ArgumentNullException(nameof(role)); } - role.Name = roleName; - return Task.CompletedTask; - } + + role.Name = roleName; + return Task.CompletedTask; + } /// - public Task GetNormalizedRoleNameAsync( + public Task GetNormalizedRoleNameAsync( UmbracoIdentityRole role, CancellationToken cancellationToken = default) => GetRoleNameAsync(role, cancellationToken); /// - public Task SetNormalizedRoleNameAsync(UmbracoIdentityRole role, string normalizedName, + public Task SetNormalizedRoleNameAsync(UmbracoIdentityRole role, string? normalizedName, CancellationToken cancellationToken = default) => SetRoleNameAsync(role, normalizedName, cancellationToken); /// - public Task FindByIdAsync(string roleId, CancellationToken cancellationToken = default) + public Task FindByIdAsync(string roleId, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); @@ -185,36 +186,36 @@ public class MemberRoleStore : IQueryableRoleStore // member group can be found by int or Guid, so try both if (!int.TryParse(roleId, NumberStyles.Integer, CultureInfo.InvariantCulture, out var id)) - { - if (!Guid.TryParse(roleId, out Guid guid)) { - throw new ArgumentOutOfRangeException(nameof(roleId), $"{nameof(roleId)} is not a valid Guid"); - } + if (!Guid.TryParse(roleId, out Guid guid)) + { + throw new ArgumentOutOfRangeException(nameof(roleId), $"{nameof(roleId)} is not a valid Guid"); + } - memberGroup = _memberGroupService.GetById(guid); + memberGroup = _memberGroupService.GetById(guid); } else { memberGroup = _memberGroupService.GetById(id); } - return Task.FromResult(memberGroup == null ? null : MapFromMemberGroup(memberGroup))!; + return Task.FromResult(memberGroup == null ? null : MapFromMemberGroup(memberGroup)); } /// - public Task FindByNameAsync(string name, CancellationToken cancellationToken = default) + public Task FindByNameAsync(string name, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentNullException(nameof(name)); - } + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException(nameof(name)); + } - IMemberGroup? memberGroup = _memberGroupService.GetByName(name); - return Task.FromResult(memberGroup == null ? null : MapFromMemberGroup(memberGroup))!; - } + IMemberGroup? memberGroup = _memberGroupService.GetByName(name); + return Task.FromResult(memberGroup == null ? null : MapFromMemberGroup(memberGroup))!; + } /// /// Dispose the store diff --git a/src/Umbraco.Infrastructure/Security/MemberUserStore.cs b/src/Umbraco.Infrastructure/Security/MemberUserStore.cs index c81b1ed36a..cd684c047a 100644 --- a/src/Umbraco.Infrastructure/Security/MemberUserStore.cs +++ b/src/Umbraco.Infrastructure/Security/MemberUserStore.cs @@ -91,7 +91,7 @@ public class MemberUserStore : UmbracoUserStore - public override Task FindByNameAsync( - string userName, - CancellationToken cancellationToken = default) + public override Task FindByNameAsync(string userName, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); IMember? user = _memberService.GetByUsername(userName); if (user == null) { - return Task.FromResult((MemberIdentityUser)null!); + return Task.FromResult(null); } - MemberIdentityUser result = AssignLoginsCallback(_mapper.Map(user))!; + MemberIdentityUser? result = AssignLoginsCallback(_mapper.Map(user))!; - return Task.FromResult(result); + return Task.FromResult(result); } public IPublishedContent? GetPublishedMember(MemberIdentityUser? user) @@ -290,7 +288,7 @@ public class MemberUserStore : UmbracoUserStore - public override Task FindByEmailAsync( + public override Task FindByEmailAsync( string email, CancellationToken cancellationToken = default) { @@ -301,11 +299,11 @@ public class MemberUserStore : UmbracoUserStore(member); - return Task.FromResult(AssignLoginsCallback(result))!; + return Task.FromResult(AssignLoginsCallback(result)); } /// - protected override Task FindUserAsync(string userId, CancellationToken cancellationToken) + protected override Task FindUserAsync(string userId, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); @@ -320,7 +318,7 @@ public class MemberUserStore : UmbracoUserStore(user)))!; @@ -440,7 +438,7 @@ public class MemberUserStore : UmbracoUserStore - protected override async Task> FindUserLoginAsync(string userId, string loginProvider, + protected override async Task?> FindUserLoginAsync(string userId, string loginProvider, string providerKey, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -456,38 +454,32 @@ public class MemberUserStore : UmbracoUserStore)null!); + return await Task.FromResult?>(null); } IList logins = await GetLoginsAsync(user, cancellationToken); UserLoginInfo? found = logins.FirstOrDefault(x => x.ProviderKey == providerKey && x.LoginProvider == loginProvider); - if (found == null) + if (found is null) { - return await Task.FromResult((IdentityUserLogin)null!); + return await Task.FromResult?>(null); } - if (user.Id is not null) + return new IdentityUserLogin { - return new IdentityUserLogin - { - LoginProvider = found.LoginProvider, - ProviderKey = found.ProviderKey, - - // TODO: We don't store this value so it will be null - ProviderDisplayName = found.ProviderDisplayName, - UserId = user.Id, - }; - } - - return null!; + LoginProvider = found.LoginProvider, + ProviderKey = found.ProviderKey, + // TODO: We don't store this value so it will be null + ProviderDisplayName = found.ProviderDisplayName, + UserId = user.Id + }; } /// - protected override Task> FindUserLoginAsync(string loginProvider, string providerKey, + protected override Task?> FindUserLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -506,11 +498,11 @@ public class MemberUserStore : UmbracoUserStore)null!); + return Task.FromResult?>(null); } IIdentityUserLogin found = logins[0]; - return Task.FromResult(new IdentityUserLogin + return Task.FromResult?>(new IdentityUserLogin { LoginProvider = found.LoginProvider, ProviderKey = found.ProviderKey, @@ -527,7 +519,7 @@ public class MemberUserStore : UmbracoUserStore currentRoles = _memberService.GetAllRoles(user.UserName); + IEnumerable currentRoles = _memberService.GetAllRoles(user.UserName!); ICollection> roles = currentRoles .Select(role => new IdentityUserRole { RoleId = role, UserId = user.Id }).ToList(); @@ -538,7 +530,7 @@ public class MemberUserStore : UmbracoUserStore /// Lists all users of a given role. /// - public override Task?> GetUsersInRoleAsync( + public override Task> GetUsersInRoleAsync( string roleName, CancellationToken cancellationToken = default) { @@ -550,10 +542,10 @@ public class MemberUserStore : UmbracoUserStore? members = _memberService.GetMembersByMemberType(roleName); + IEnumerable members = _memberService.GetMembersByMemberType(roleName); - IList? membersIdentityUsers = - members?.Select(x => _mapper.Map(x)!).ToList(); + IList membersIdentityUsers = + members.Select(x => _mapper.Map(x)!).ToList(); return Task.FromResult(membersIdentityUsers); } @@ -567,8 +559,7 @@ public class MemberUserStore : UmbracoUserStore /// - public override Task SetTokenAsync(MemberIdentityUser user, string loginProvider, string name, string value, - CancellationToken cancellationToken) + public override Task SetTokenAsync(MemberIdentityUser user, string loginProvider, string name, string? value, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); @@ -626,7 +617,7 @@ public class MemberUserStore : UmbracoUserStore - protected override Task FindRoleAsync(string roleName, CancellationToken cancellationToken) + protected override Task FindRoleAsync(string roleName, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(roleName)) { @@ -634,12 +625,12 @@ public class MemberUserStore : UmbracoUserStore x.Name == roleName); - if (group == null) + if (group?.Name is null) { - return Task.FromResult((UmbracoIdentityRole)null!); + return Task.FromResult(null); } - return Task.FromResult(new UmbracoIdentityRole(group.Name) + return Task.FromResult(new UmbracoIdentityRole(group.Name) { // TODO: what should the alias be? Id = group.Id.ToString(), @@ -647,27 +638,25 @@ public class MemberUserStore : UmbracoUserStore - protected override async Task> FindUserRoleAsync(string userId, string roleId, + protected override async Task?> FindUserRoleAsync(string userId, string roleId, CancellationToken cancellationToken) { - MemberIdentityUser user = await FindUserAsync(userId, cancellationToken); - if (user == null) + MemberIdentityUser? user = await FindUserAsync(userId, cancellationToken); + if (user is null) { - return null!; + return null; } IdentityUserRole? found = user.Roles.FirstOrDefault(x => x.RoleId.InvariantEquals(roleId)); - return found!; + return found; } private MemberIdentityUser? AssignLoginsCallback(MemberIdentityUser? user) { - if (user != null) + if (user is not null) { - user.SetLoginsCallback( - new Lazy?>(() => _externalLoginService.GetExternalLogins(user.Key))); - user.SetTokensCallback(new Lazy?>(() => - _externalLoginService.GetExternalLoginTokens(user.Key))); + user.SetLoginsCallback(new Lazy?>(() => _externalLoginService.GetExternalLogins(user.Key))); + user.SetTokensCallback(new Lazy?>(() => _externalLoginService.GetExternalLoginTokens(user.Key))); } return user; @@ -729,7 +718,7 @@ public class MemberUserStore : UmbracoUserStore public class NoopLookupNormalizer : ILookupNormalizer { - public string NormalizeName(string name) => name; + public string? NormalizeName(string? name) => name; - public string NormalizeEmail(string email) => email; + public string? NormalizeEmail(string? email) => email; } diff --git a/src/Umbraco.Infrastructure/Security/UmbracoErrorDescriberBase.cs b/src/Umbraco.Infrastructure/Security/UmbracoErrorDescriberBase.cs index 671483fe7b..ade4fd1515 100644 --- a/src/Umbraco.Infrastructure/Security/UmbracoErrorDescriberBase.cs +++ b/src/Umbraco.Infrastructure/Security/UmbracoErrorDescriberBase.cs @@ -34,7 +34,7 @@ public abstract class UmbracoErrorDescriberBase : IdentityErrorDescriber Description = _textService.Localize("validation", "duplicateUsername", new[] { userName }), }; - public override IdentityError InvalidEmail(string email) => new() + public override IdentityError InvalidEmail(string? email) => new() { Code = nameof(InvalidEmail), Description = _textService.Localize("validation", "invalidEmail"), @@ -46,7 +46,7 @@ public abstract class UmbracoErrorDescriberBase : IdentityErrorDescriber Description = _textService.Localize("validation", "invalidToken"), }; - public override IdentityError InvalidUserName(string userName) => new() + public override IdentityError InvalidUserName(string? userName) => new() { Code = nameof(InvalidUserName), Description = _textService.Localize("validation", "invalidUsername"), diff --git a/src/Umbraco.Infrastructure/Security/UmbracoIdentityRole.cs b/src/Umbraco.Infrastructure/Security/UmbracoIdentityRole.cs index e38a0b89ab..af6e745f46 100644 --- a/src/Umbraco.Infrastructure/Security/UmbracoIdentityRole.cs +++ b/src/Umbraco.Infrastructure/Security/UmbracoIdentityRole.cs @@ -6,10 +6,10 @@ namespace Umbraco.Cms.Core.Security; public class UmbracoIdentityRole : IdentityRole, IRememberBeingDirty { - private string? _id; - private string? _name; + private string _id = string.Empty; + private string _name = string.Empty; - public UmbracoIdentityRole(string? roleName) + public UmbracoIdentityRole(string roleName) : base(roleName) { } @@ -26,7 +26,7 @@ public class UmbracoIdentityRole : IdentityRole, IRememberBeingDirty } /// - public override string? Id + public override string Id { get => _id; set @@ -40,11 +40,11 @@ public class UmbracoIdentityRole : IdentityRole, IRememberBeingDirty public override string? Name { get => _name; - set => BeingDirty.SetPropertyValueAndDetectChanges(value, ref _name, nameof(Name)); + set => BeingDirty.SetPropertyValueAndDetectChanges(value, ref _name!, nameof(Name)); } /// - public override string NormalizedName { get => base.Name; set => base.Name = value; } + public override string? NormalizedName { get => base.Name ?? string.Empty; set => base.Name = value; } /// /// Gets or sets a value indicating whether returns an Id has been set on this object this will be false if the object @@ -58,7 +58,7 @@ public class UmbracoIdentityRole : IdentityRole, IRememberBeingDirty // model. A good writeup of that is here: // https://stackoverflow.com/a/37362173 // For our purposes currently we won't worry about this. - public override string ConcurrencyStamp { get => base.ConcurrencyStamp; set => base.ConcurrencyStamp = value; } + public override string? ConcurrencyStamp { get => base.ConcurrencyStamp; set => base.ConcurrencyStamp = value; } /// /// Gets the for change tracking diff --git a/src/Umbraco.Infrastructure/Security/UmbracoIdentityUser.cs b/src/Umbraco.Infrastructure/Security/UmbracoIdentityUser.cs index bf79ab602d..7a44933e46 100644 --- a/src/Umbraco.Infrastructure/Security/UmbracoIdentityUser.cs +++ b/src/Umbraco.Infrastructure/Security/UmbracoIdentityUser.cs @@ -71,7 +71,7 @@ public abstract class UmbracoIdentityUser : IdentityUser, IRememberBeingDirty // model. A good writeup of that is here: // https://stackoverflow.com/a/37362173 // For our purposes currently we won't worry about this. - public override string ConcurrencyStamp { get => base.ConcurrencyStamp; set => base.ConcurrencyStamp = value; } + public override string? ConcurrencyStamp { get => base.ConcurrencyStamp; set => base.ConcurrencyStamp = value; } /// /// Gets or sets last login date @@ -85,7 +85,7 @@ public abstract class UmbracoIdentityUser : IdentityUser, IRememberBeingDirty /// /// Gets or sets email /// - public override string Email + public override string? Email { get => _email; set => BeingDirty.SetPropertyValueAndDetectChanges(value, ref _email!, nameof(Email)); @@ -247,7 +247,7 @@ public abstract class UmbracoIdentityUser : IdentityUser, IRememberBeingDirty /// /// Gets or sets user name /// - public override string UserName + public override string? UserName { get => _userName; set => BeingDirty.SetPropertyValueAndDetectChanges(value, ref _userName!, nameof(UserName)); diff --git a/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs b/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs index bc8b8078ca..dd3572c928 100644 --- a/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs +++ b/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs @@ -134,8 +134,8 @@ public abstract class UmbracoUserManager : UserManager 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) + // we cannot proceed if the user passed in does not have an identity, or if no password is provided. + if (user.HasIdentity == false || password is null) { return false; } @@ -158,10 +158,10 @@ public abstract class UmbracoUserManager : UserManager - 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) + TUser? user = await FindByIdAsync(userId); + if (user is null) { throw new InvalidOperationException("Could not find user"); } @@ -251,8 +251,8 @@ public abstract class UmbracoUserManager : UserManager ValidateCredentialsAsync(string username, string password) { - TUser user = await FindByNameAsync(username); - if (user == null) + TUser? user = await FindByNameAsync(username); + if (user is null) { return false; } diff --git a/src/Umbraco.Infrastructure/Security/UmbracoUserStore.cs b/src/Umbraco.Infrastructure/Security/UmbracoUserStore.cs index 7a6c9d793a..35a8f2eea9 100644 --- a/src/Umbraco.Infrastructure/Security/UmbracoUserStore.cs +++ b/src/Umbraco.Infrastructure/Security/UmbracoUserStore.cs @@ -84,7 +84,7 @@ public abstract class UmbracoUserStore } /// - public override Task FindByIdAsync(string userId, CancellationToken cancellationToken = default) => + public override Task FindByIdAsync(string userId, CancellationToken cancellationToken = default) => FindUserAsync(userId, cancellationToken); /// @@ -96,11 +96,11 @@ public abstract class UmbracoUserStore throw new NotImplementedException(); /// - public override Task GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken) + public override Task GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken) => GetEmailAsync(user, cancellationToken); /// - public override Task GetNormalizedUserNameAsync(TUser user, CancellationToken cancellationToken = default) + public override Task GetNormalizedUserNameAsync(TUser user, CancellationToken cancellationToken = default) => GetUserNameAsync(user, cancellationToken); /// @@ -221,15 +221,15 @@ public abstract class UmbracoUserStore public override Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default) => throw new NotImplementedException(); /// - public override Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken) + public override Task SetNormalizedEmailAsync(TUser user, string? normalizedEmail, CancellationToken cancellationToken) => SetEmailAsync(user, normalizedEmail, cancellationToken); /// - public override Task SetNormalizedUserNameAsync(TUser user, string normalizedName, CancellationToken cancellationToken = default) + public override Task SetNormalizedUserNameAsync(TUser user, string? normalizedName, CancellationToken cancellationToken = default) => SetUserNameAsync(user, normalizedName, cancellationToken); /// - public override async Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken cancellationToken = default) + public override async Task SetPasswordHashAsync(TUser user, string? passwordHash, CancellationToken cancellationToken = default) { await base.SetPasswordHashAsync(user, passwordHash, cancellationToken); user.LastPasswordChangeDateUtc = DateTime.UtcNow; @@ -247,7 +247,7 @@ public abstract class UmbracoUserStore /// /// [EditorBrowsable(EditorBrowsableState.Never)] - protected override Task> FindTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) => throw new NotImplementedException(); + protected override Task?> FindTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) => throw new NotImplementedException(); /// /// Not supported in Umbraco, see comments above on GetTokenAsync, RemoveTokenAsync, SetTokenAsync diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 7d48468a96..7e038b17cf 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 Umbraco.Cms.Infrastructure Umbraco.Cms.Infrastructure Umbraco CMS Infrastructure @@ -24,6 +24,11 @@ + + + + + @@ -50,6 +55,9 @@ + + + diff --git a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj index 0eb68b99fe..2d82addc84 100644 --- a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj +++ b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 Umbraco.Cms.Infrastructure.PublishedCache Umbraco.Cms.PublishedCache.NuCache Umbraco CMS Published Cache diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs index 390482276e..7704344d4e 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs @@ -186,11 +186,13 @@ public class AuthenticationController : UmbracoApiControllerBase [ValidateAngularAntiForgeryToken] public async Task PostUnLinkLogin(UnLinkLoginModel unlinkLoginModel) { - BackOfficeIdentityUser? user = await _userManager.FindByIdAsync(User.Identity?.GetUserId()); - if (user == null) + var userId = User.Identity?.GetUserId(); + if (userId is null) { - throw new InvalidOperationException("Could not find user"); + throw new InvalidOperationException("Could not find userId"); } + var user = await _userManager.FindByIdAsync(userId); + if (user == null) throw new InvalidOperationException("Could not find user"); AuthenticationScheme? authType = (await _signInManager.GetExternalAuthenticationSchemesAsync()) .FirstOrDefault(x => x.Name == unlinkLoginModel.LoginProvider); @@ -484,16 +486,19 @@ public class AuthenticationController : UmbracoApiControllerBase UmbracoUserExtensions.GetUserCulture(user.Culture, _textService, _globalSettings), new[] { code }); - if (provider == "Email") - { - var mailMessage = new EmailMessage(from, user.Email, subject, message, true); - - await _emailSender.SendAsync(mailMessage, Constants.Web.EmailTypes.TwoFactorAuth); - } - else if (provider == "Phone") - { - await _smsSender.SendSmsAsync(await _userManager.GetPhoneNumberAsync(user), message); - } + if (provider == "Email") + { + var mailMessage = new EmailMessage(from, user.Email, subject, message, true); + await _emailSender.SendAsync(mailMessage, Constants.Web.EmailTypes.TwoFactorAuth); + } + else if (provider == "Phone") + { + var phoneNumber = await _userManager.GetPhoneNumberAsync(user); + if (phoneNumber is not null) + { + await _smsSender.SendSmsAsync(phoneNumber, message); + } + } return Ok(); } @@ -544,6 +549,10 @@ public class AuthenticationController : UmbracoApiControllerBase { BackOfficeIdentityUser? identityUser = await _userManager.FindByIdAsync(model.UserId.ToString(CultureInfo.InvariantCulture)); + if (identityUser is null) + { + return new ValidationErrorResult("Could not find user"); + } IdentityResult result = await _userManager.ResetPasswordAsync(identityUser, model.ResetCode, model.Password); if (result.Succeeded) diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index beee83cbb4..640a03f447 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -379,7 +379,7 @@ public class BackOfficeController : UmbracoController [HttpGet] public async Task ExternalLinkLoginCallback() { - BackOfficeIdentityUser user = await _userManager.GetUserAsync(User); + BackOfficeIdentityUser? user = await _userManager.GetUserAsync(User); if (user == null) { // ... this should really not happen @@ -497,9 +497,8 @@ public class BackOfficeController : UmbracoController } else if (result == SignInResult.TwoFactorRequired) { - BackOfficeIdentityUser? attemptedUser = - await _userManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey); - if (attemptedUser == null) + BackOfficeIdentityUser? attemptedUser = await _userManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey); + if (attemptedUser?.UserName is null) { return new ValidationErrorResult( $"No local user found for the login provider {loginInfo.LoginProvider} - {loginInfo.ProviderKey}"); diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs index b220b0fcd1..c24bbc8a20 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs @@ -376,7 +376,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers }, { "currentUserApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( - controller => controller.PostChangePassword(new ChangingPasswordModel())) + controller => controller.PostSetAvatar(new List())) }, { "entityApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( diff --git a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs index 0c155f5209..87f881ff69 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs @@ -198,12 +198,13 @@ public class CurrentUserController : UmbracoAuthorizedJsonController [AllowAnonymous] public async Task> PostSetInvitedUserPassword([FromBody] string newPassword) { - BackOfficeIdentityUser? user = await _backOfficeUserManager.FindByIdAsync(_backofficeSecurityAccessor - .BackOfficeSecurity?.GetUserId().ResultOr(0).ToString()); - if (user == null) + var userId = _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().ResultOr(0).ToString(); + if (userId is null) { - throw new InvalidOperationException("Could not find user"); + throw new InvalidOperationException("Could not find user Id"); } + var user = await _backOfficeUserManager.FindByIdAsync(userId); + if (user == null) throw new InvalidOperationException("Could not find user"); IdentityResult result = await _backOfficeUserManager.AddPasswordAsync(user, newPassword); @@ -303,8 +304,18 @@ public class CurrentUserController : UmbracoAuthorizedJsonController [ValidateAngularAntiForgeryToken] public async Task> GetCurrentUserLinkedLogins() { - BackOfficeIdentityUser identityUser = await _backOfficeUserManager.FindByIdAsync(_backofficeSecurityAccessor - .BackOfficeSecurity?.GetUserId().ResultOr(0).ToString(CultureInfo.InvariantCulture)); + var userId = _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().ResultOr(0).ToString(CultureInfo.InvariantCulture); + if (userId is null) + { + throw new InvalidOperationException("Could not find user Id"); + } + + BackOfficeIdentityUser? identityUser = await _backOfficeUserManager.FindByIdAsync(userId); + + if (identityUser is null) + { + throw new InvalidOperationException("Could not find user"); + } // deduplicate in case there are duplicates (there shouldn't be now since we have a unique constraint on the external logins // but there didn't used to be) diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs index 70f337f44f..b7220a3941 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs @@ -367,7 +367,7 @@ public class MemberController : ContentControllerBase contentItem.IsApproved, contentItem.Name); - IdentityResult created = await _memberManager.CreateAsync(identityMember, contentItem.Password?.NewPassword); + IdentityResult created = await _memberManager.CreateAsync(identityMember, contentItem.Password?.NewPassword!); if (created.Succeeded == false) { @@ -513,8 +513,12 @@ public class MemberController : ContentControllerBase } var needsResync = false; - - MemberIdentityUser identityMember = await _memberManager.FindByIdAsync(contentItem.Id?.ToString()); + var memberId = contentItem.Id?.ToString(); + if (memberId is null) + { + return ValidationProblem("Member was not found"); + } + MemberIdentityUser? identityMember = await _memberManager.FindByIdAsync(memberId); if (identityMember == null) { return ValidationProblem("Member was not found"); diff --git a/src/Umbraco.Web.BackOffice/Controllers/TwoFactorLoginController.cs b/src/Umbraco.Web.BackOffice/Controllers/TwoFactorLoginController.cs index 79e5f32707..68a8773362 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/TwoFactorLoginController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/TwoFactorLoginController.cs @@ -65,7 +65,11 @@ public class TwoFactorLoginController : UmbracoAuthorizedJsonController [HttpGet] public async Task>> Get2FAProvidersForUser(int userId) { - BackOfficeIdentityUser user = await _backOfficeUserManager.FindByIdAsync(userId.ToString(CultureInfo.InvariantCulture)); + BackOfficeIdentityUser? user = await _backOfficeUserManager.FindByIdAsync(userId.ToString(CultureInfo.InvariantCulture)); + if (user is null) + { + throw new InvalidOperationException("Could not find user"); + } var enabledProviderNameHashSet = new HashSet(await _twoFactorLoginService.GetEnabledTwoFactorProviderNamesAsync(user.Key)); diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs index f734d8626b..916ff3d495 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs @@ -565,10 +565,20 @@ public class UsersController : BackOfficeNotificationsController return new ActionResult(user); } - private async Task SendUserInviteEmailAsync(UserBasic? userDisplay, string? from, string? fromEmail, IUser? to, - string? message) + private async Task SendUserInviteEmailAsync(UserBasic? userDisplay, string? from, string? fromEmail, IUser? to, string? message) { - BackOfficeIdentityUser user = await _userManager.FindByIdAsync(((int?)userDisplay?.Id).ToString()); + var userId = userDisplay?.Id?.ToString(); + if (userId is null) + { + throw new InvalidOperationException("Could not find user Id"); + } + var user = await _userManager.FindByIdAsync(userId); + + if (user is null) + { + throw new InvalidOperationException("Could not find user"); + } + var token = await _userManager.GenerateEmailConfirmationTokenAsync(user); // Use info from SMTP Settings if configured, otherwise set fromEmail as fallback diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs index 54e25240e0..9d677bce66 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs @@ -48,7 +48,7 @@ namespace Umbraco.Extensions IEnumerable userLangFileSources = contentFileProvider.GetDirectoryContents(userConfigLangFolder) .Where(x => x.IsDirectory && x.Name.InvariantEquals("lang")) - .Select(x => new DirectoryInfo(x.PhysicalPath)) + .Select(x => new DirectoryInfo(x.PhysicalPath!)) .SelectMany(x => x.GetFiles("*.user.xml", SearchOption.TopDirectoryOnly)) .Select(x => new LocalizedTextServiceSupplementaryFileSource(x, true)); @@ -87,7 +87,7 @@ namespace Umbraco.Extensions .GetDirectoryContents(langFolder) .Where(x => !string.IsNullOrEmpty(x.PhysicalPath)) .Where(x => x.Name.InvariantEndsWith(".xml")) - .Select(x => new FileInfo(x.PhysicalPath)); + .Select(x => new FileInfo(x.PhysicalPath!)); foreach (FileInfo file in localizationFiles) { diff --git a/src/Umbraco.Web.BackOffice/Install/CreateUnattendedUserNotificationHandler.cs b/src/Umbraco.Web.BackOffice/Install/CreateUnattendedUserNotificationHandler.cs index 13896c8912..1ac7f20819 100644 --- a/src/Umbraco.Web.BackOffice/Install/CreateUnattendedUserNotificationHandler.cs +++ b/src/Umbraco.Web.BackOffice/Install/CreateUnattendedUserNotificationHandler.cs @@ -75,7 +75,7 @@ public class CreateUnattendedUserNotificationHandler : INotificationAsyncHandler using IServiceScope scope = _serviceScopeFactory.CreateScope(); IBackOfficeUserManager backOfficeUserManager = scope.ServiceProvider.GetRequiredService(); - BackOfficeIdentityUser membershipUser = + BackOfficeIdentityUser? membershipUser = await backOfficeUserManager.FindByIdAsync(Constants.Security.SuperUserIdAsString); if (membershipUser == null) { diff --git a/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs b/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs index 3dfc9f51b1..ab1b19dde8 100644 --- a/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs +++ b/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs @@ -88,9 +88,11 @@ public class InstallApiController : ControllerBase { await _runtime.RestartAsync(); - BackOfficeIdentityUser identityUser = - await _backOfficeUserManager.FindByIdAsync(Constants.Security.SuperUserIdAsString); - _backOfficeSignInManager.SignInAsync(identityUser, false); + BackOfficeIdentityUser? identityUser = await _backOfficeUserManager.FindByIdAsync(Constants.Security.SuperUserIdAsString); + if (identityUser is not null) + { + _backOfficeSignInManager.SignInAsync(identityUser, false); + } return NoContent(); } diff --git a/src/Umbraco.Web.BackOffice/Middleware/ConfigureGlobalOptionsForKeepAliveMiddlware.cs b/src/Umbraco.Web.BackOffice/Middleware/ConfigureGlobalOptionsForKeepAliveMiddlware.cs index 15366ec113..5156ad3af5 100644 --- a/src/Umbraco.Web.BackOffice/Middleware/ConfigureGlobalOptionsForKeepAliveMiddlware.cs +++ b/src/Umbraco.Web.BackOffice/Middleware/ConfigureGlobalOptionsForKeepAliveMiddlware.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; namespace Umbraco.Cms.Web.BackOffice.Middleware; @@ -18,6 +18,6 @@ public sealed class ConfigureGlobalOptionsForKeepAliveMiddlware : IPostConfigure /// /// /// - public void PostConfigure(string name, GlobalSettings options) => + public void PostConfigure(string? name, GlobalSettings options) => options.ReservedUrls += _keepAliveSettings.Value.KeepAlivePingUrl; } diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs index 24217d331b..f4aed22fbe 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs @@ -62,10 +62,10 @@ public class BackOfficeAuthenticationBuilder : AuthenticationBuilder internal class EnsureBackOfficeScheme : IPostConfigureOptions where TOptions : RemoteAuthenticationOptions { - public void PostConfigure(string name, TOptions options) + public void PostConfigure(string? name, TOptions options) { // ensure logic only applies to backoffice authentication schemes - if (name.StartsWith(Constants.Security.BackOfficeExternalAuthenticationTypePrefix)) + if (name is not null && name.StartsWith(Constants.Security.BackOfficeExternalAuthenticationTypePrefix)) { options.SignInScheme = Constants.Security.BackOfficeExternalAuthenticationType; } diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeSessionIdValidator.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeSessionIdValidator.cs index 15413db2a7..f0cb1beee5 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeSessionIdValidator.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeSessionIdValidator.cs @@ -126,7 +126,12 @@ public class BackOfficeSessionIdValidator } var userId = currentIdentity.GetUserId(); - BackOfficeIdentityUser? user = await _userManager.FindByIdAsync(userId); + if (userId is null) + { + return false; + } + + var user = await _userManager.FindByIdAsync(userId); if (user == null) { return false; diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs index 74f5fb5eb8..19a231cf52 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs @@ -117,7 +117,7 @@ public class BackOfficeSignInManager : UmbracoSignInManagerThe external login URL users should be redirected to during the login flow. /// The current user's identifier, which will be used to provide CSRF protection. /// A configured . - public override AuthenticationProperties ConfigureExternalAuthenticationProperties(string provider, string? redirectUrl, string? userId = null) + public override AuthenticationProperties ConfigureExternalAuthenticationProperties(string? provider, string? redirectUrl, string? userId = null) { // borrowed from https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs // to be able to use our own XsrfKey/LoginProviderKey because the default is private :/ @@ -197,7 +197,7 @@ public class BackOfficeSignInManager : UmbracoSignInManager - public void Configure(string name, CookieAuthenticationOptions options) + public void Configure(string? name, CookieAuthenticationOptions options) { if (name != Constants.Security.BackOfficeAuthenticationType) { diff --git a/src/Umbraco.Web.BackOffice/Security/PasswordChanger.cs b/src/Umbraco.Web.BackOffice/Security/PasswordChanger.cs index 59ea9a7b81..25f0548386 100644 --- a/src/Umbraco.Web.BackOffice/Security/PasswordChanger.cs +++ b/src/Umbraco.Web.BackOffice/Security/PasswordChanger.cs @@ -52,7 +52,7 @@ internal class PasswordChanger : IPasswordChanger where TUser : Um } var userId = changingPasswordModel.Id.ToString(); - TUser identityUser = await userMgr.FindByIdAsync(userId); + TUser? identityUser = await userMgr.FindByIdAsync(userId); if (identityUser == null) { // this really shouldn't ever happen... but just in case @@ -96,7 +96,7 @@ internal class PasswordChanger : IPasswordChanger where TUser : Um } // can we change to the new password? - IdentityResult changeResult = await userMgr.ChangePasswordAsync(identityUser, changingPasswordModel.OldPassword, changingPasswordModel.NewPassword); + IdentityResult changeResult = await userMgr.ChangePasswordAsync(identityUser, changingPasswordModel.OldPassword!, changingPasswordModel.NewPassword); if (changeResult.Succeeded == false) { // no, fail with error messages for "password" diff --git a/src/Umbraco.Web.BackOffice/Services/IconService.cs b/src/Umbraco.Web.BackOffice/Services/IconService.cs index 7f060dc756..fa6bf3eb4c 100644 --- a/src/Umbraco.Web.BackOffice/Services/IconService.cs +++ b/src/Umbraco.Web.BackOffice/Services/IconService.cs @@ -141,7 +141,7 @@ public class IconService : IIconService IEnumerable coreIcons = iconFolder .Where(x => !x.IsDirectory && x.Name.EndsWith(".svg")) - .Select(x => new FileInfo(x.PhysicalPath)); + .Select(x => new FileInfo(x.PhysicalPath!)); icons.UnionWith(coreIcons); diff --git a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj index 3f24e2717e..6d8cc02c1a 100644 --- a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj +++ b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 Library Umbraco.Cms.Web.BackOffice Umbraco.Cms.Web.BackOffice diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs index 57f1e288b7..af8fec7f69 100644 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs @@ -82,7 +82,7 @@ public class AspNetCoreHostingEnvironment : IHostingEnvironment public Uri ApplicationMainUrl { get; private set; } = null!; /// - public string SiteName { get; private set; } = null!; + public string? SiteName { get; private set; } /// public string ApplicationId diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs index 38d67ff2f0..abb2b71a4e 100644 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs @@ -44,10 +44,10 @@ public class AspNetCoreRequestAccessor : IRequestAccessor, INotificationHandler< }); /// - public string GetRequestValue(string name) => GetFormValue(name) ?? GetQueryStringValue(name); + public string? GetRequestValue(string name) => GetFormValue(name) ?? GetQueryStringValue(name); /// - public string GetQueryStringValue(string name) => _httpContextAccessor.GetRequiredHttpContext().Request.Query[name]; + public string? GetQueryStringValue(string name) => _httpContextAccessor.GetRequiredHttpContext().Request.Query[name]; /// public Uri? GetRequestUrl() => _httpContextAccessor.HttpContext != null diff --git a/src/Umbraco.Web.Common/AspNetCore/OptionsMonitorAdapter.cs b/src/Umbraco.Web.Common/AspNetCore/OptionsMonitorAdapter.cs index 112841a722..fd3d3516af 100644 --- a/src/Umbraco.Web.Common/AspNetCore/OptionsMonitorAdapter.cs +++ b/src/Umbraco.Web.Common/AspNetCore/OptionsMonitorAdapter.cs @@ -15,7 +15,7 @@ internal class OptionsMonitorAdapter : IOptionsMonitor public T CurrentValue { get; } - public T Get(string name) => CurrentValue; + public T Get(string? name) => CurrentValue; public IDisposable OnChange(Action listener) => throw new NotImplementedException(); } diff --git a/src/Umbraco.Web.Common/Extensions/FormCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/FormCollectionExtensions.cs index c91d0fb6c2..e8961dba0a 100644 --- a/src/Umbraco.Web.Common/Extensions/FormCollectionExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/FormCollectionExtensions.cs @@ -49,7 +49,7 @@ public static class FormCollectionExtensions /// /// /// - public static string GetRequiredString(this FormCollection items, string key) + public static string? GetRequiredString(this FormCollection items, string key) { if (items.HasKey(key) == false) { diff --git a/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs b/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs index 226755039e..0f2da0ac4e 100644 --- a/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs @@ -65,7 +65,7 @@ public static class HttpContextExtensions /// /// Get the value in the request form or query string for the key /// - public static string GetRequestValue(this HttpContext context, string key) + public static string? GetRequestValue(this HttpContext context, string key) { HttpRequest request = context.Request; if (!request.HasFormContentType) @@ -73,7 +73,7 @@ public static class HttpContextExtensions return request.Query[key]; } - string value = request.Form[key]; + string? value = request.Form[key]; return value ?? request.Query[key]; } diff --git a/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs b/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs index 44fa64b274..4abbb21cb3 100644 --- a/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs +++ b/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs @@ -70,7 +70,7 @@ public sealed class RoutableDocumentFilter : IRoutableDocumentFilter return maybeDoc; } - private void EndpointsChanged(object value) + private void EndpointsChanged(object? value) { lock (_routeLocker) { diff --git a/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs b/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs index 09793081cf..352136504e 100644 --- a/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs @@ -90,7 +90,7 @@ public class BackOfficeUserManager : UmbracoUserManager 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) @@ -101,7 +101,7 @@ public class BackOfficeUserManager : UmbracoUserManager 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) diff --git a/src/Umbraco.Web.Common/Security/ConfigureMemberCookieOptions.cs b/src/Umbraco.Web.Common/Security/ConfigureMemberCookieOptions.cs index 1e0960fbc7..5bf7a98749 100644 --- a/src/Umbraco.Web.Common/Security/ConfigureMemberCookieOptions.cs +++ b/src/Umbraco.Web.Common/Security/ConfigureMemberCookieOptions.cs @@ -18,7 +18,7 @@ public sealed class ConfigureMemberCookieOptions : IConfigureNamedOptions { - ClaimsIdentity newIdentity = refreshingPrincipal.NewPrincipal.Identities.First(); - ClaimsIdentity currentIdentity = refreshingPrincipal.CurrentPrincipal.Identities.First(); + ClaimsIdentity? newIdentity = refreshingPrincipal.NewPrincipal?.Identities.First(); + ClaimsIdentity? currentIdentity = refreshingPrincipal.CurrentPrincipal?.Identities.First(); - // Since this is refreshing an existing principal, we want to merge all claims. - newIdentity.MergeAllClaims(currentIdentity); + if (currentIdentity is not null) + { + // Since this is refreshing an existing principal, we want to merge all claims. + newIdentity?.MergeAllClaims(currentIdentity); + } return Task.CompletedTask; }; diff --git a/src/Umbraco.Web.Common/Security/IBackOfficeSignInManager.cs b/src/Umbraco.Web.Common/Security/IBackOfficeSignInManager.cs index 8e674c8b9f..f76912743b 100644 --- a/src/Umbraco.Web.Common/Security/IBackOfficeSignInManager.cs +++ b/src/Umbraco.Web.Common/Security/IBackOfficeSignInManager.cs @@ -19,7 +19,7 @@ public interface IBackOfficeSignInManager Task GetExternalLoginInfoAsync(string? expectedXsrf = null); - Task GetTwoFactorAuthenticationUserAsync(); + Task GetTwoFactorAuthenticationUserAsync(); Task PasswordSignInAsync(string userName, string password, bool isPersistent, bool lockoutOnFailure); @@ -29,7 +29,7 @@ public interface IBackOfficeSignInManager Task CreateUserPrincipalAsync(BackOfficeIdentityUser user); - Task TwoFactorSignInAsync(string? provider, string? code, bool isPersistent, bool rememberClient); + Task TwoFactorSignInAsync(string provider, string code, bool isPersistent, bool rememberClient); Task UpdateExternalAuthenticationTokensAsync(ExternalLoginInfo externalLogin); } diff --git a/src/Umbraco.Web.Common/Security/IMemberSignInManager.cs b/src/Umbraco.Web.Common/Security/IMemberSignInManager.cs index a5a444bd06..27d034930d 100644 --- a/src/Umbraco.Web.Common/Security/IMemberSignInManager.cs +++ b/src/Umbraco.Web.Common/Security/IMemberSignInManager.cs @@ -21,7 +21,7 @@ public interface IMemberSignInManager Task ExternalLoginSignInAsync(ExternalLoginInfo loginInfo, bool isPersistent, bool bypassTwoFactor = false); - Task GetTwoFactorAuthenticationUserAsync(); + Task GetTwoFactorAuthenticationUserAsync(); - Task TwoFactorSignInAsync(string? provider, string? code, bool isPersistent, bool rememberClient); + Task TwoFactorSignInAsync(string provider, string code, bool isPersistent, bool rememberClient); } diff --git a/src/Umbraco.Web.Common/Security/MemberManager.cs b/src/Umbraco.Web.Common/Security/MemberManager.cs index 19be3de489..c59f0bd86e 100644 --- a/src/Umbraco.Web.Common/Security/MemberManager.cs +++ b/src/Umbraco.Web.Common/Security/MemberManager.cs @@ -188,7 +188,7 @@ public class MemberManager : UmbracoUserManager HasAccessAsync(string path) { MemberIdentityUser? currentMember = await GetCurrentMemberAsync(); - if (currentMember == null || !currentMember.IsApproved || currentMember.IsLockedOut) + if (currentMember?.UserName is null || !currentMember.IsApproved || currentMember.IsLockedOut) { return false; } @@ -220,7 +220,7 @@ public class MemberManager : UmbracoUserManager(); MemberIdentityUser? currentMember = await GetCurrentMemberAsync(); - if (currentMember == null || !currentMember.IsApproved || currentMember.IsLockedOut) + if (currentMember?.UserName is null || !currentMember.IsApproved || currentMember.IsLockedOut) { return result; } diff --git a/src/Umbraco.Web.Common/Security/MemberSignInManager.cs b/src/Umbraco.Web.Common/Security/MemberSignInManager.cs index a624129bab..393d05e9a0 100644 --- a/src/Umbraco.Web.Common/Security/MemberSignInManager.cs +++ b/src/Umbraco.Web.Common/Security/MemberSignInManager.cs @@ -159,8 +159,8 @@ public class MemberSignInManager : UmbracoSignInManager, IMe } public override AuthenticationProperties ConfigureExternalAuthenticationProperties( - string provider, - string redirectUrl, + string? provider, + string? redirectUrl, string? userId = null) { // borrowed from https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs @@ -213,7 +213,7 @@ public class MemberSignInManager : UmbracoSignInManager, IMe } // Now we need to perform the auto-link, so first we need to lookup/create a user with the email address - MemberIdentityUser? autoLinkUser = await UserManager.FindByEmailAsync(email); + MemberIdentityUser? autoLinkUser = await UserManager.FindByEmailAsync(email!); if (autoLinkUser != null) { try @@ -244,7 +244,7 @@ public class MemberSignInManager : UmbracoSignInManager, IMe throw new InvalidOperationException("The Name value cannot be null"); } - autoLinkUser = MemberIdentityUser.CreateNew(email, email, autoLinkOptions.DefaultMemberTypeAlias, autoLinkOptions.DefaultIsApproved, name); + autoLinkUser = MemberIdentityUser.CreateNew(email!, email!, autoLinkOptions.DefaultMemberTypeAlias, autoLinkOptions.DefaultIsApproved, name); foreach (var userGroup in autoLinkOptions.DefaultMemberGroups) { diff --git a/src/Umbraco.Web.Common/Security/PublicAccessChecker.cs b/src/Umbraco.Web.Common/Security/PublicAccessChecker.cs index b8b662ef2b..9777f56d7d 100644 --- a/src/Umbraco.Web.Common/Security/PublicAccessChecker.cs +++ b/src/Umbraco.Web.Common/Security/PublicAccessChecker.cs @@ -47,7 +47,7 @@ public class PublicAccessChecker : IPublicAccessChecker return PublicAccessStatus.LockedOut; } - if (!_publicAccessService.HasAccess(publishedContentId, _contentService, username, userRoles)) + if (!_publicAccessService.HasAccess(publishedContentId, _contentService, username!, userRoles)) { return PublicAccessStatus.AccessDenied; } diff --git a/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs b/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs index 63defed2de..052a3ae631 100644 --- a/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs +++ b/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs @@ -86,7 +86,7 @@ public abstract class UmbracoSignInManager : SignInManager var providerKey = auth.Principal.FindFirstValue(ClaimTypes.NameIdentifier); var provider = items[UmbracoSignInMgrLoginProviderKey]; - if (providerKey == null || provider == null) + if (providerKey is null || provider is null) { return null; } @@ -102,14 +102,14 @@ public abstract class UmbracoSignInManager : SignInManager } /// - public override async Task GetTwoFactorAuthenticationUserAsync() + public override async Task GetTwoFactorAuthenticationUserAsync() { // borrowed from https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs // replaced in order to use a custom auth type TwoFactorAuthenticationInfo? info = await RetrieveTwoFactorInfoAsync(); - if (info == null) + if (info?.UserId is null) { - return null!; + return null; } return await UserManager.FindByIdAsync(info.UserId); @@ -142,7 +142,7 @@ public abstract class UmbracoSignInManager : SignInManager } /// - public override async Task TwoFactorSignInAsync(string? provider, string? code, bool isPersistent, bool rememberClient) + public override async Task TwoFactorSignInAsync(string provider, string code, bool isPersistent, bool rememberClient) { // borrowed from https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs#L552 // replaced in order to use a custom auth type and to implement logging/events diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index c75ddfe98f..83aa5bb7bf 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 Library Umbraco.Cms.Web.Common Umbraco.Cms.Web.Common @@ -9,7 +9,7 @@ Contains the Web assembly needed to run Umbraco Cms. This package only contains the assembly, and can be used for package development. Use the template in the Umbraco.Templates package to setup Umbraco - net6.0 + net7.0 Library Umbraco.Cms.Web.Common Umbraco.Cms.Web.Common @@ -35,6 +35,8 @@ + + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 2ec54bc2d8..a30d782a43 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -1,22 +1,38 @@ - net6.0 + net7.0 Umbraco.Cms.Web.UI false + + + + bin/Release/Umbraco.Web.UI.xml + + + + true + + - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + @@ -46,6 +62,8 @@ + + @@ -59,4 +77,5 @@ + diff --git a/src/Umbraco.Web.Website/Controllers/UmbExternalLoginController.cs b/src/Umbraco.Web.Website/Controllers/UmbExternalLoginController.cs index 1ea68f40aa..2cbda678de 100644 --- a/src/Umbraco.Web.Website/Controllers/UmbExternalLoginController.cs +++ b/src/Umbraco.Web.Website/Controllers/UmbExternalLoginController.cs @@ -111,7 +111,7 @@ public class UmbExternalLoginController : SurfaceController if (result == SignInResult.TwoFactorRequired) { - MemberIdentityUser attemptedUser = + MemberIdentityUser? attemptedUser = await _memberManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey); if (attemptedUser == null!) { @@ -218,7 +218,7 @@ public class UmbExternalLoginController : SurfaceController [HttpGet] public async Task ExternalLinkLoginCallback(string returnUrl) { - MemberIdentityUser user = await _memberManager.GetUserAsync(User); + MemberIdentityUser? user = await _memberManager.GetUserAsync(User); string? loginProvider = null; var errors = new List(); if (user == null!) @@ -272,10 +272,20 @@ public class UmbExternalLoginController : SurfaceController returnUrl = Request.GetEncodedPathAndQuery(); } - MemberIdentityUser user = await _memberManager.FindByIdAsync(User.Identity?.GetUserId()); + var userId = User.Identity?.GetUserId(); + if (userId is null) + { + return CurrentUmbracoPage(); + } + + MemberIdentityUser? user = await _memberManager.FindByIdAsync(userId); + + if (user is null) + { + return CurrentUmbracoPage(); + } IdentityResult result = await _memberManager.RemoveLoginAsync(user, provider, providerKey); - if (result.Succeeded) { await _memberSignInManager.SignInAsync(user, false); @@ -283,6 +293,7 @@ public class UmbExternalLoginController : SurfaceController } AddModelErrors(result); + return CurrentUmbracoPage(); } } diff --git a/src/Umbraco.Web.Website/Controllers/UmbLoginController.cs b/src/Umbraco.Web.Website/Controllers/UmbLoginController.cs index 046849612a..c195b52409 100644 --- a/src/Umbraco.Web.Website/Controllers/UmbLoginController.cs +++ b/src/Umbraco.Web.Website/Controllers/UmbLoginController.cs @@ -81,7 +81,7 @@ public class UmbLoginController : SurfaceController if (result.RequiresTwoFactor) { - MemberIdentityUser attemptedUser = await _memberManager.FindByNameAsync(model.Username); + MemberIdentityUser? attemptedUser = await _memberManager.FindByNameAsync(model.Username); if (attemptedUser == null!) { return new ValidationErrorResult( diff --git a/src/Umbraco.Web.Website/Models/ProfileModelBuilder.cs b/src/Umbraco.Web.Website/Models/ProfileModelBuilder.cs index 982fb127cd..70f034fc79 100644 --- a/src/Umbraco.Web.Website/Models/ProfileModelBuilder.cs +++ b/src/Umbraco.Web.Website/Models/ProfileModelBuilder.cs @@ -51,7 +51,7 @@ public class ProfileModelBuilder : MemberModelBuilderBase ? null : await memberManager.GetUserAsync(_httpContextAccessor.HttpContext.User); - if (member == null) + if (member?.Email is null || member?.UserName is null) { return null; } diff --git a/src/Umbraco.Web.Website/Security/MemberAuthenticationBuilder.cs b/src/Umbraco.Web.Website/Security/MemberAuthenticationBuilder.cs index 0029e0b80a..50e6f80b78 100644 --- a/src/Umbraco.Web.Website/Security/MemberAuthenticationBuilder.cs +++ b/src/Umbraco.Web.Website/Security/MemberAuthenticationBuilder.cs @@ -62,9 +62,9 @@ public class MemberAuthenticationBuilder : AuthenticationBuilder private class EnsureMemberScheme : IPostConfigureOptions where TOptions : RemoteAuthenticationOptions { - public void PostConfigure(string name, TOptions options) + public void PostConfigure(string? name, TOptions options) { - if (!name.StartsWith(Constants.Security.MemberExternalAuthenticationTypePrefix)) + if (name is null || !name.StartsWith(Constants.Security.MemberExternalAuthenticationTypePrefix)) { return; } diff --git a/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj b/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj index 176564eaf8..94f3bfa479 100644 --- a/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj +++ b/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 Library Umbraco.Cms.Web.Website Umbraco.Cms.Web.Website diff --git a/templates/Umbraco.Templates.csproj b/templates/Umbraco.Templates.csproj index 47b2246835..6f06115dc4 100644 --- a/templates/Umbraco.Templates.csproj +++ b/templates/Umbraco.Templates.csproj @@ -3,7 +3,7 @@ - net6.0 + net7.0 Template false . diff --git a/templates/UmbracoPackage/UmbracoPackage.csproj b/templates/UmbracoPackage/UmbracoPackage.csproj index cdab2646e0..92c1e78d82 100644 --- a/templates/UmbracoPackage/UmbracoPackage.csproj +++ b/templates/UmbracoPackage/UmbracoPackage.csproj @@ -1,6 +1,6 @@  - net6.0 + net7.0 . UmbracoPackage UmbracoPackage diff --git a/templates/UmbracoProject/UmbracoProject.csproj b/templates/UmbracoProject/UmbracoProject.csproj index 2880b754e6..c2234b0f1e 100644 --- a/templates/UmbracoProject/UmbracoProject.csproj +++ b/templates/UmbracoProject/UmbracoProject.csproj @@ -1,6 +1,6 @@ - net6.0 + net7.0 enable enable Umbraco.Cms.Web.UI diff --git a/tests/Umbraco.TestData/Umbraco.TestData.csproj b/tests/Umbraco.TestData/Umbraco.TestData.csproj index b5b2c5c99f..b2c9fa7236 100644 --- a/tests/Umbraco.TestData/Umbraco.TestData.csproj +++ b/tests/Umbraco.TestData/Umbraco.TestData.csproj @@ -3,7 +3,7 @@ false Umbraco.TestData - net6.0 + net7.0 diff --git a/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker b/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker index 5ae033d6d3..2efbb9a2da 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker +++ b/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker @@ -2,7 +2,7 @@ ## Build ############################################ -FROM mcr.microsoft.com/dotnet/sdk:6.0.300 AS build +FROM mcr.microsoft.com/dotnet/nightly/sdk:7.0 AS build WORKDIR /nupkg COPY nupkg . @@ -17,7 +17,7 @@ RUN dotnet publish --no-restore --configuration Release -o /dist ## Run ############################################ -FROM mcr.microsoft.com/dotnet/aspnet:6.0.5 AS run +FROM mcr.microsoft.com/dotnet/nightly/aspnet:7.0 AS run WORKDIR /cypress COPY --from=build dist . diff --git a/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 29e0a36353..500f53cb89 100644 --- a/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -1,6 +1,6 @@ - net6.0 + net7.0 Exe false false @@ -26,7 +26,7 @@ 0.13.1 - 6.0.0 + 7.0.0-preview.7.22375.6 4.18.1 diff --git a/tests/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj b/tests/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj index 05a5554db2..25a6559123 100644 --- a/tests/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj +++ b/tests/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 Umbraco.Cms.Tests.Common Umbraco.Cms.Tests Umbraco CMS Test Tools diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index 8397da51b4..4d859d3625 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 Umbraco.Cms.Tests.Integration Umbraco.Cms.Tests.Integration Umbraco CMS Integration Tests @@ -86,8 +86,8 @@ + - diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/ModelTypeTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/ModelTypeTests.cs index 1176b7f571..d850d00d3d 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/ModelTypeTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/ModelTypeTests.cs @@ -46,7 +46,7 @@ public class ModelTypeTests // Note the inner assembly qualified name Assert.AreEqual( - "System.Collections.Generic.IEnumerable`1[[System.Int32[], System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", + "System.Collections.Generic.IEnumerable`1[[System.Int32[], System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", typeof(IEnumerable<>).MakeGenericType(type.MakeArrayType()).FullName); } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberPasswordHasherTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberPasswordHasherTests.cs index 8e8b39bb42..9d26be6ab6 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberPasswordHasherTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberPasswordHasherTests.cs @@ -15,10 +15,11 @@ public class MemberPasswordHasherTests [Test] [TestCase( "Password123!", - "AQAAAAEAACcQAAAAEGF/tTVoL6ef3bQPZFYfbgKFu1CDQIAMgyY1N4EDt9jqdG/hsOX93X1U6LNvlIQ3mw==", + "AQAAAAIAAYagAAAAEJBorDjt+UEvw55UJVsbgAS6T2IGao+2XpCBbO3EKZoAMzoN+CNOpPdu1c0qrFcJVw==", null, ExpectedResult = PasswordVerificationResult.Success, Description = "AspNetCoreIdentityPasswordHash: Correct password")] + [TestCase("Password123!", "AQAAAAEAACcQAAAAEGF/tTVoL6ef3bQPZFYfbgKFu1CDQIAMgyY1N4EDt9jqdG/hsOX93X1U6LNvlIQ3mw==", null, - ExpectedResult = PasswordVerificationResult.Success, - Description = "AspNetCoreIdentityPasswordHash: Correct password")] + ExpectedResult = PasswordVerificationResult.SuccessRehashNeeded, + Description = "GivenALegacyAspNetCoreIdentityPasswordHash: Correct password")] [TestCase( "wrongPassword", "AQAAAAEAACcQAAAAEGF/tTVoL6ef3bQPZFYfbgKFu1CDQIAMgyY1N4EDt9jqdG/hsOX93X1U6LNvlIQ3mw==", diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj index 55d875d1bc..fdc40c3e2b 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net7.0 Umbraco.Cms.Tests.UnitTests false true @@ -26,8 +26,8 @@ - - + +