diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index d0778279dc..35f794f895 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -15,7 +15,7 @@ permissions:
contents: read
env:
- dotnetVersion: 7.x
+ dotnetVersion: 8.x
dotnetIncludePreviewVersions: true
solution: umbraco.sln
buildConfiguration: SkipTests
diff --git a/Directory.Build.props b/Directory.Build.props
index 7d9f7ed111..087cea7abc 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,8 +1,8 @@
- net7.0
- latest
+ net8.0
+ 11.0
Umbraco HQ
Umbraco
Copyright © Umbraco $([System.DateTime]::Today.ToString('yyyy'))
@@ -30,8 +30,8 @@
true
- true
- 11.0.0
+ false
+ 12.0.0-rc1
true
true
diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml
index b33e4a6cc0..24c62b90aa 100644
--- a/build/azure-pipelines.yml
+++ b/build/azure-pipelines.yml
@@ -24,7 +24,7 @@ parameters:
variables:
nodeVersion: 18.16.0
- dotnetVersion: 7.x
+ dotnetVersion: 8.x
dotnetIncludePreviewVersions: true
solution: umbraco.sln
buildConfiguration: Release
diff --git a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj
index 946c08556f..cc1571540e 100644
--- a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj
+++ b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj
@@ -6,7 +6,7 @@
-
+
diff --git a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Umbraco.Cms.Persistence.EFCore.Sqlite.csproj b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Umbraco.Cms.Persistence.EFCore.Sqlite.csproj
index f95f1cd1e1..d3274a5005 100644
--- a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Umbraco.Cms.Persistence.EFCore.Sqlite.csproj
+++ b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Umbraco.Cms.Persistence.EFCore.Sqlite.csproj
@@ -6,7 +6,7 @@
-
+
diff --git a/src/Umbraco.Cms.Persistence.EFCore/Umbraco.Cms.Persistence.EFCore.csproj b/src/Umbraco.Cms.Persistence.EFCore/Umbraco.Cms.Persistence.EFCore.csproj
index f8e3851ccd..8d7a835ab1 100644
--- a/src/Umbraco.Cms.Persistence.EFCore/Umbraco.Cms.Persistence.EFCore.csproj
+++ b/src/Umbraco.Cms.Persistence.EFCore/Umbraco.Cms.Persistence.EFCore.csproj
@@ -6,8 +6,9 @@
-
-
+
+
+
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 45b6b01052..92d929ce12 100644
--- a/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj
+++ b/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml
index 049d7477e7..f621d27fa6 100644
--- a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml
+++ b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml
@@ -719,6 +719,7 @@
af
Fortryd
Celle margen
+ Skift
Vælg
Ryd
Luk
diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml
index c7ad610ef0..38c4916e31 100644
--- a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml
+++ b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml
@@ -759,6 +759,7 @@
by
Cancel
Cell margin
+ Change
Choose
Clear
Close
diff --git a/src/Umbraco.Core/Models/ContentEditing/ContentVariationDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/ContentVariationDisplay.cs
index 6418a7bca7..0d359b640f 100644
--- a/src/Umbraco.Core/Models/ContentEditing/ContentVariationDisplay.cs
+++ b/src/Umbraco.Core/Models/ContentEditing/ContentVariationDisplay.cs
@@ -14,6 +14,7 @@ public class ContentVariantDisplay : ITabbedContent, ICo
Tabs = new List>();
Notifications = new List();
AllowedActions = Enumerable.Empty();
+ AdditionalPreviewUrls = Enumerable.Empty();
}
[DataMember(Name = "allowedActions", IsRequired = true)]
@@ -72,6 +73,9 @@ public class ContentVariantDisplay : ITabbedContent, ICo
///
[DataMember(Name = "tabs")]
public IEnumerable> Tabs { get; set; }
+
+ [DataMember(Name = "additionalPreviewUrls")]
+ public IEnumerable AdditionalPreviewUrls { get; set; }
}
public class ContentVariantScheduleDisplay : ContentVariantDisplay
diff --git a/src/Umbraco.Core/Models/ContentEditing/NamedUrl.cs b/src/Umbraco.Core/Models/ContentEditing/NamedUrl.cs
new file mode 100644
index 0000000000..8c71f86cbf
--- /dev/null
+++ b/src/Umbraco.Core/Models/ContentEditing/NamedUrl.cs
@@ -0,0 +1,13 @@
+using System.Runtime.Serialization;
+
+namespace Umbraco.Cms.Core.Models.ContentEditing;
+
+[DataContract(Name = "namedUrl", Namespace = "")]
+public class NamedUrl
+{
+ [DataMember(Name = "name")]
+ public required string Name { get; set; }
+
+ [DataMember(Name = "url")]
+ public required string Url { get; set; }
+}
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 30d7b8ce7f..6a385aeba6 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -7,18 +7,18 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj
index a683fdae9c..c527c9efa2 100644
--- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj
+++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj
@@ -13,15 +13,14 @@
-
-
-
-
-
-
+
+
+
+
+
@@ -40,8 +39,8 @@
-
-
+
+
diff --git a/src/Umbraco.Web.BackOffice/Mapping/ContentMapDefinition.cs b/src/Umbraco.Web.BackOffice/Mapping/ContentMapDefinition.cs
index e91384f86a..21961acb45 100644
--- a/src/Umbraco.Web.BackOffice/Mapping/ContentMapDefinition.cs
+++ b/src/Umbraco.Web.BackOffice/Mapping/ContentMapDefinition.cs
@@ -127,6 +127,7 @@ internal class ContentMapDefinition : IMapDefinition
target.Tabs = source.Tabs;
target.UpdateDate = source.UpdateDate;
target.AllowedActions = source.AllowedActions;
+ target.AdditionalPreviewUrls = source.AdditionalPreviewUrls;
}
// Umbraco.Code.MapAll
@@ -189,6 +190,7 @@ internal class ContentMapDefinition : IMapDefinition
target.Tabs = source.Tabs;
target.UpdateDate = source.UpdateDate;
target.AllowedActions = source.AllowedActions;
+ target.AdditionalPreviewUrls = source.AdditionalPreviewUrls;
// We'll only try and map the ReleaseDate/ExpireDate if the "old" ContentVariantScheduleDisplay is in the context, otherwise we'll just skip it quietly.
_ = context.Items.TryGetValue(nameof(ContentItemDisplayWithSchedule.Variants), out var variants);
@@ -352,7 +354,7 @@ internal class ContentMapDefinition : IMapDefinition
return result;
}
- // Umbraco.Code.MapAll -Segment -Language -DisplayName
+ // Umbraco.Code.MapAll -Segment -Language -DisplayName -AdditionalPreviewUrls
private void Map(IContent source, ContentVariantDisplay target, MapperContext context)
{
target.CreateDate = source.CreateDate;
diff --git a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj
index 00e6c46273..90104b30a4 100644
--- a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj
+++ b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj
@@ -7,10 +7,6 @@
Umbraco.Cms.Web.BackOffice
-
-
-
-
@@ -33,4 +29,4 @@
-
\ No newline at end of file
+
diff --git a/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs b/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs
index 95c4ae5cec..2f56cdb51f 100644
--- a/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs
+++ b/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs
@@ -60,14 +60,14 @@ public class UmbracoMemberAuthorizeFilter : IAsyncAuthorizationFilter
{
context.HttpContext.SetReasonPhrase(
"Resource restricted: the member is not of a permitted type or group.");
+ context.HttpContext.Response.StatusCode = 403;
context.Result = new ForbidResult();
}
}
else
{
- context.HttpContext.SetReasonPhrase(
- "Resource restricted: the member is not logged in.");
- context.Result = new UnauthorizedResult();
+ context.HttpContext.Response.StatusCode = 401;
+ context.Result = new ForbidResult();
}
}
diff --git a/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CompilationOptionsProvider.cs b/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CompilationOptionsProvider.cs
index 0dac2084bd..b2c02e54b2 100644
--- a/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CompilationOptionsProvider.cs
+++ b/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CompilationOptionsProvider.cs
@@ -193,18 +193,7 @@ internal class CompilationOptionsProvider
var parseOptions = new CSharpParseOptions(preprocessorSymbols: (IEnumerable)defines);
- if (string.IsNullOrEmpty(dependencyContextOptions.LanguageVersion))
- {
- parseOptions = parseOptions.WithLanguageVersion(LanguageVersion.Latest);
- }
- else if (LanguageVersionFacts.TryParse(dependencyContextOptions.LanguageVersion, out var languageVersion))
- {
- parseOptions = parseOptions.WithLanguageVersion(languageVersion);
- }
- else
- {
- Debug.Fail($"LanguageVersion {languageVersion} specified in the deps file could not be parsed.");
- }
+ parseOptions = parseOptions.WithLanguageVersion(LanguageVersion.Latest);
return parseOptions;
}
diff --git a/src/Umbraco.Web.Common/Mvc/IpAddressUtilities.cs b/src/Umbraco.Web.Common/Mvc/IpAddressUtilities.cs
index 876c1fdd3f..319669571c 100644
--- a/src/Umbraco.Web.Common/Mvc/IpAddressUtilities.cs
+++ b/src/Umbraco.Web.Common/Mvc/IpAddressUtilities.cs
@@ -7,11 +7,25 @@ public class IpAddressUtilities : IIpAddressUtilities
{
public bool IsAllowListed(IPAddress clientIpAddress, string allowedIpString)
{
- if (IPNetwork.TryParse(allowedIpString, out IPNetwork allowedIp) && allowedIp.Contains(clientIpAddress))
+ var subnetmaskIndex = allowedIpString.LastIndexOf('/');
+ if (subnetmaskIndex >= 0) // It's a network
+ {
+ if (IPNetwork.TryParse(allowedIpString, out IPNetwork allowedIp) && allowedIp.Contains(clientIpAddress))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ // Assume ip address
+ if (IPAddress.TryParse(allowedIpString, out IPAddress? allowedIpAddress) && allowedIpAddress.Equals(clientIpAddress))
{
return true;
}
return false;
+
+
}
}
diff --git a/src/Umbraco.Web.Common/Security/ConfigureMemberCookieOptions.cs b/src/Umbraco.Web.Common/Security/ConfigureMemberCookieOptions.cs
index b8c2874641..1ba9a52526 100644
--- a/src/Umbraco.Web.Common/Security/ConfigureMemberCookieOptions.cs
+++ b/src/Umbraco.Web.Common/Security/ConfigureMemberCookieOptions.cs
@@ -1,10 +1,12 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Routing;
using Umbraco.Cms.Core.Services;
+using Umbraco.Cms.Web.Common.Controllers;
using Umbraco.Extensions;
namespace Umbraco.Cms.Web.Common.Security;
@@ -58,7 +60,16 @@ public sealed class ConfigureMemberCookieOptions : IConfigureNamedOptions
{
- ctx.Response.StatusCode = StatusCodes.Status403Forbidden;
+ // When the controller is an UmbracoAPIController, we want to return a StatusCode instead of a redirect.
+ // All other cases should use the default Redirect of the CookieAuthenticationEvent.
+ var controllerDescriptor = ctx.HttpContext.GetEndpoint()?.Metadata
+ .OfType()
+ .FirstOrDefault();
+
+ if (!controllerDescriptor?.ControllerTypeInfo.IsSubclassOf(typeof(UmbracoApiController)) ?? false)
+ {
+ new CookieAuthenticationEvents().OnRedirectToAccessDenied(ctx);
+ }
return Task.CompletedTask;
},
diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj
index faea7d7e48..dc5fdde359 100644
--- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj
+++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj
@@ -6,16 +6,12 @@
Umbraco.Cms.Web.Common
-
-
-
-
-
-
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/assets/img/login.svg b/src/Umbraco.Web.UI.Client/src/assets/img/login.svg
index 3bd280b4af..37499a996c 100644
--- a/src/Umbraco.Web.UI.Client/src/assets/img/login.svg
+++ b/src/Umbraco.Web.UI.Client/src/assets/img/login.svg
@@ -1 +1,996 @@
-
\ No newline at end of file
+
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js b/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js
index abf173b129..238d9a8ee6 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js
@@ -344,10 +344,14 @@ function clipboardService($window, notificationsService, eventsService, localSto
// Clean up each entry
var copiedDatas = datas.map(data => prepareEntryForStorage(type, data, firstLevelClearupMethod));
- // remove previous copies of this entry:
+ // remove previous copies of this entry (Make sure to not remove copies from unsaved content):
storage.entries = storage.entries.filter(
(entry) => {
- return entry.unique !== uniqueKey;
+ if (entry.unique === 0) {
+ return displayLabel !== entry.label;
+ } else {
+ return entry.unique !== uniqueKey;
+ }
}
);
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html
index 56c7a9cf48..be6f21ed96 100644
--- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html
+++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-sub-views.html
@@ -5,7 +5,7 @@
ng-repeat="subView in subViews track by subView.alias"
ng-class="'sub-view-' + subView.name"
val-sub-view="subView"
- ng-if="subView.active"
+ ng-show="subView.active"
>
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
index e0db466443..44a72b7380 100644
--- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
+++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
@@ -25,6 +25,7 @@
+
all
diff --git a/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj b/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj
index 0d4a775a95..f9f3779b47 100644
--- a/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj
+++ b/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj
@@ -7,10 +7,6 @@
Umbraco.Cms.Web.Website
-
-
-
-
diff --git a/templates/UmbracoPackage/.template.config/template.json b/templates/UmbracoPackage/.template.config/template.json
index 768a7a4bee..33ec0699b1 100644
--- a/templates/UmbracoPackage/.template.config/template.json
+++ b/templates/UmbracoPackage/.template.config/template.json
@@ -28,13 +28,13 @@
"datatype": "choice",
"choices": [
{
- "displayName": ".NET 7.0",
- "description": "Target net7.0",
- "choice": "net7.0"
+ "displayName": ".NET 8.0",
+ "description": "Target net8.0",
+ "choice": "net8.0"
}
],
- "defaultValue": "net7.0",
- "replaces": "net7.0"
+ "defaultValue": "net8.0",
+ "replaces": "net8.0"
},
"UmbracoVersion": {
"displayName": "Umbraco version",
diff --git a/templates/UmbracoPackage/UmbracoPackage.csproj b/templates/UmbracoPackage/UmbracoPackage.csproj
index 6da6cf2a79..268611b9ae 100644
--- a/templates/UmbracoPackage/UmbracoPackage.csproj
+++ b/templates/UmbracoPackage/UmbracoPackage.csproj
@@ -1,6 +1,6 @@
- net7.0
+ net8.0
.
UmbracoPackage
UmbracoPackage
diff --git a/templates/UmbracoPackageRcl/UmbracoPackage.csproj b/templates/UmbracoPackageRcl/UmbracoPackage.csproj
index 5c980684ce..1cbdd209e5 100644
--- a/templates/UmbracoPackageRcl/UmbracoPackage.csproj
+++ b/templates/UmbracoPackageRcl/UmbracoPackage.csproj
@@ -1,6 +1,6 @@
- net7.0
+ net8.0
enable
enable
true
diff --git a/templates/UmbracoProject/.template.config/template.json b/templates/UmbracoProject/.template.config/template.json
index d88b23c07d..b17352476e 100644
--- a/templates/UmbracoProject/.template.config/template.json
+++ b/templates/UmbracoProject/.template.config/template.json
@@ -38,13 +38,13 @@
"datatype": "choice",
"choices": [
{
- "displayName": ".NET 7.0",
- "description": "Target net7.0",
- "choice": "net7.0"
+ "displayName": ".NET 8.0",
+ "description": "Target net8.0",
+ "choice": "net8.0"
}
],
- "defaultValue": "net7.0",
- "replaces": "net7.0"
+ "defaultValue": "net8.0",
+ "replaces": "net8.0"
},
"UmbracoVersion": {
"displayName": "Umbraco version",
diff --git a/templates/UmbracoProject/UmbracoProject.csproj b/templates/UmbracoProject/UmbracoProject.csproj
index d50f95a907..fcd25050f6 100644
--- a/templates/UmbracoProject/UmbracoProject.csproj
+++ b/templates/UmbracoProject/UmbracoProject.csproj
@@ -1,6 +1,6 @@
- net7.0
+ net8.0
enable
enable
Umbraco.Cms.Web.UI
diff --git a/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker b/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker
index e19483cfa1..d1ca7fb4a4 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/nightly/sdk:7.0 AS build
+FROM mcr.microsoft.com/dotnet/nightly/sdk:8.0.100-preview.6-jammy AS build
COPY nuget.config .
@@ -22,7 +22,7 @@ RUN dotnet publish --configuration Release --no-build --output /dist
## Run
############################################
-FROM mcr.microsoft.com/dotnet/nightly/aspnet:7.0 AS run
+FROM mcr.microsoft.com/dotnet/nightly/aspnet:8.0.0-preview.6-jammy AS run
WORKDIR /app
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 f338b443f3..e24c03c2c3 100644
--- a/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj
+++ b/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj
index 5ea936bc58..145300ef37 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj
@@ -11,8 +11,8 @@
-
-
+
+
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Web.Website/Security/MemberAuthorizeTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Web.Website/Security/MemberAuthorizeTests.cs
new file mode 100644
index 0000000000..0fc1dfa85d
--- /dev/null
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Web.Website/Security/MemberAuthorizeTests.cs
@@ -0,0 +1,126 @@
+using System.Net;
+using Microsoft.AspNetCore.Authentication.Cookies;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Cms.Core.Cache;
+using Umbraco.Cms.Core.Logging;
+using Umbraco.Cms.Core.Routing;
+using Umbraco.Cms.Core.Security;
+using Umbraco.Cms.Core.Services;
+using Umbraco.Cms.Core.Web;
+using Umbraco.Cms.Infrastructure.Persistence;
+using Umbraco.Cms.Tests.Integration.TestServerTest;
+using Umbraco.Cms.Web.Common.Controllers;
+using Umbraco.Cms.Web.Common.Filters;
+using Umbraco.Cms.Web.Common.Security;
+using Umbraco.Cms.Web.Website.Controllers;
+
+namespace Umbraco.Cms.Tests.Integration.Umbraco.Web.Website.Security
+{
+ public class MemberAuthorizeTests : UmbracoTestServerTestBase
+ {
+ private Mock _memberManagerMock = new();
+
+ protected override void ConfigureTestServices(IServiceCollection services)
+ {
+ _memberManagerMock = new Mock();
+ services.Remove(new ServiceDescriptor(typeof(IMemberManager), typeof(MemberManager), ServiceLifetime.Scoped));
+ services.Remove(new ServiceDescriptor(typeof(MemberManager), ServiceLifetime.Scoped));
+ services.AddScoped(_ => _memberManagerMock.Object);
+ }
+
+ [Test]
+ public async Task Secure_SurfaceController_Should_Return_Redirect_WhenNotLoggedIn()
+ {
+ _memberManagerMock.Setup(x => x.IsLoggedIn()).Returns(false);
+
+ var url = PrepareSurfaceControllerUrl(x => x.Secure());
+
+ var response = await Client.GetAsync(url);
+
+ var cookieAuthenticationOptions = Services.GetService>();
+ Assert.AreEqual(HttpStatusCode.Redirect, response.StatusCode);
+ Assert.AreEqual(cookieAuthenticationOptions.Value.AccessDeniedPath.ToString(), response.Headers.Location?.AbsolutePath);
+ }
+
+ [Test]
+ public async Task Secure_SurfaceController_Should_Return_Redirect_WhenNotAuthorized()
+ {
+ _memberManagerMock.Setup(x => x.IsLoggedIn()).Returns(true);
+ _memberManagerMock.Setup(x => x.IsMemberAuthorizedAsync(
+ It.IsAny>(),
+ It.IsAny>(),
+ It.IsAny>()))
+ .ReturnsAsync(false);
+
+ var url = PrepareSurfaceControllerUrl(x => x.Secure());
+
+ var response = await Client.GetAsync(url);
+
+ var cookieAuthenticationOptions = Services.GetService>();
+ Assert.AreEqual(HttpStatusCode.Redirect, response.StatusCode);
+ Assert.AreEqual(cookieAuthenticationOptions.Value.AccessDeniedPath.ToString(), response.Headers.Location?.AbsolutePath);
+ }
+
+
+ [Test]
+ public async Task Secure_ApiController_Should_Return_Unauthorized_WhenNotLoggedIn()
+ {
+ _memberManagerMock.Setup(x => x.IsLoggedIn()).Returns(false);
+ var url = PrepareApiControllerUrl(x => x.Secure());
+
+ var response = await Client.GetAsync(url);
+
+ Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode);
+ }
+
+ [Test]
+ public async Task Secure_ApiController_Should_Return_Forbidden_WhenNotAuthorized()
+ {
+ _memberManagerMock.Setup(x => x.IsLoggedIn()).Returns(true);
+ _memberManagerMock.Setup(x => x.IsMemberAuthorizedAsync(
+ It.IsAny>(),
+ It.IsAny>(),
+ It.IsAny>()))
+ .ReturnsAsync(false);
+
+ var url = PrepareApiControllerUrl(x => x.Secure());
+
+ var response = await Client.GetAsync(url);
+
+ Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode);
+ }
+ }
+
+ public class TestSurfaceController : SurfaceController
+ {
+ public TestSurfaceController(
+ IUmbracoContextAccessor umbracoContextAccessor,
+ IUmbracoDatabaseFactory databaseFactory,
+ ServiceContext services,
+ AppCaches appCaches,
+ IProfilingLogger profilingLogger,
+ IPublishedUrlProvider publishedUrlProvider)
+ : base(
+ umbracoContextAccessor,
+ databaseFactory,
+ services,
+ appCaches,
+ profilingLogger,
+ publishedUrlProvider)
+ {
+ }
+
+ [UmbracoMemberAuthorize]
+ public IActionResult Secure() => NoContent();
+ }
+
+ public class TestApiController : UmbracoApiController
+ {
+ [UmbracoMemberAuthorize]
+ public IActionResult Secure() => NoContent();
+ }
+}
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs
index d312428734..ca47bfbd97 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs
@@ -153,11 +153,20 @@ public class ComponentTests
return new ProfilingLogger(Mock.Of>(), Mock.Of());
}
+ if (type == typeof(ILogger))
+ {
+ return Mock.Of>();
+ }
+
if (type == typeof(ILogger))
{
return Mock.Of>();
}
+ if (type == typeof(IServiceProviderIsService))
+ {
+ return Mock.Of();
+ }
throw new NotSupportedException(type.FullName);
});
});
@@ -315,6 +324,11 @@ public class ComponentTests
{
return Mock.Of>();
}
+
+ if (type == typeof(IServiceProviderIsService))
+ {
+ return Mock.Of();
+ }
throw new NotSupportedException(type.FullName);
});
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/ModelTypeTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/ModelTypeTests.cs
index eaab03c9b8..c2cbc1e416 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/ModelTypeTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/ModelTypeTests.cs
@@ -1,7 +1,7 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
-using System.Collections.Generic;
+using System.Collections;
using NUnit.Framework;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Tests.Common.Published;
@@ -45,7 +45,7 @@ public class ModelTypeTests
// Note the inner assembly qualified name
Assert.AreEqual(
- "System.Collections.Generic.IEnumerable`1[[System.Int32[], System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]",
+ $"System.Collections.Generic.IEnumerable`1[[System.Int32[], System.Private.CoreLib, Version={typeof(IEnumerable).Assembly.GetName().Version}, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]",
typeof(IEnumerable<>).MakeGenericType(type.MakeArrayType()).FullName);
}
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/BackOffice/BackOfficeClaimsPrincipalFactoryTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/BackOffice/BackOfficeClaimsPrincipalFactoryTests.cs
index 1a8cecee6b..afda7c5cbf 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/BackOffice/BackOfficeClaimsPrincipalFactoryTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/BackOffice/BackOfficeClaimsPrincipalFactoryTests.cs
@@ -64,8 +64,8 @@ public class BackOfficeClaimsPrincipalFactoryTests
new BackOfficeClaimsPrincipalFactory(GetMockedUserManager().Object, null));
[Test]
- public void Ctor_When_Options_Value_Is_Null_Expect_ArgumentNullException()
- => Assert.Throws(() => new BackOfficeClaimsPrincipalFactory(
+ public void Ctor_When_Options_Value_Is_Null_Expect_ArgumentException()
+ => Assert.Throws(() => new BackOfficeClaimsPrincipalFactory(
GetMockedUserManager().Object,
new OptionsWrapper(null)));
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj
index 5efd49eedf..7d37843ce0 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj
@@ -7,8 +7,8 @@
+
-