diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml
index dd7d1947fd..8fefe3ef4d 100644
--- a/build/azure-pipelines.yml
+++ b/build/azure-pipelines.yml
@@ -85,23 +85,6 @@ stages:
retryCountOnTaskFailure: 3
inputs:
versionSpec: $(nodeVersion)
- - task: Cache@2
- displayName: Cache node_modules
- inputs:
- key: '"npm_client" | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Umbraco.Web.UI.Client/package-lock.json'
- restoreKeys: |
- "npm_client" | "$(Agent.OS)"
- "npm_client"
- path: $(npm_config_cache)
- - script: npm ci --no-fund --no-audit --prefer-offline
- workingDirectory: src/Umbraco.Web.UI.Client
- displayName: Run npm ci (Backoffice)
- - task: gulp@0
- displayName: Run gulp build (Backoffice)
- inputs:
- gulpFile: src/Umbraco.Web.UI.Client/gulpfile.js
- targets: coreBuild
- workingDirectory: src/Umbraco.Web.UI.Client
- script: npm ci --no-fund --no-audit --prefer-offline
displayName: Run npm ci (Login)
workingDirectory: src/Umbraco.Web.UI.Login
@@ -241,7 +224,7 @@ stages:
- task: Cache@2
displayName: Cache node_modules
inputs:
- key: '"npm_client" | "$(Agent.OS)"| $(Build.SourcesDirectory)/src/Umbraco.Web.UI.Client/package-lock.json | $(Build.SourcesDirectory)/src/Umbraco.Web.UI.New.Client/package-lock.json'
+ key: '"npm_client" | "$(Agent.OS)"| $(Build.SourcesDirectory)/src/Umbraco.Web.UI.New.Client/package-lock.json'
restoreKeys: |
"npm_client" | "$(Agent.OS)"
"npm_client"
diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs
index d0b68d81d2..d58bdc148a 100644
--- a/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs
+++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs
@@ -2,6 +2,7 @@
using Asp.Versioning;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
@@ -14,11 +15,11 @@ using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Security;
-using Umbraco.Cms.Web.BackOffice.Security;
using Umbraco.Extensions;
using IdentitySignInResult = Microsoft.AspNetCore.Identity.SignInResult;
using SignInResult = Microsoft.AspNetCore.Mvc.SignInResult;
using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Web.Common.Security;
namespace Umbraco.Cms.Api.Management.Controllers.Security;
@@ -174,7 +175,7 @@ public class BackOfficeController : SecurityControllerBase
// Returning a SignOutResult will ask OpenIddict to redirect the user agent
// to the post_logout_redirect_uri specified by the client application.
- return SignOut(Constants.Security.NewBackOfficeAuthenticationType, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
+ return SignOut(Constants.Security.BackOfficeAuthenticationType, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
///
@@ -182,7 +183,7 @@ public class BackOfficeController : SecurityControllerBase
///
private async Task GetUserNameFromAuthCookie()
{
- AuthenticateResult cookieAuthResult = await HttpContext.AuthenticateAsync(Constants.Security.NewBackOfficeAuthenticationType);
+ AuthenticateResult cookieAuthResult = await HttpContext.AuthenticateAsync(Constants.Security.BackOfficeAuthenticationType);
return cookieAuthResult.Succeeded
? cookieAuthResult.Principal?.Identity?.Name
: null;
@@ -263,5 +264,5 @@ public class BackOfficeController : SecurityControllerBase
return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, backOfficePrincipal);
}
- private static IActionResult DefaultChallengeResult() => new ChallengeResult(Constants.Security.NewBackOfficeAuthenticationType);
+ private static IActionResult DefaultChallengeResult() => new ChallengeResult(Constants.Security.BackOfficeAuthenticationType);
}
diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeDefaultController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeDefaultController.cs
new file mode 100644
index 0000000000..e9ef58d8b0
--- /dev/null
+++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeDefaultController.cs
@@ -0,0 +1,39 @@
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Umbraco.Cms.Core;
+using Umbraco.Extensions;
+
+namespace Umbraco.Cms.Api.Management.Controllers.Security;
+
+public class BackOfficeDefaultController : Controller
+{
+ [HttpGet]
+ [AllowAnonymous]
+ public async Task Index()
+ {
+ // force authentication to occur since this is not an authorized endpoint
+ AuthenticateResult result = await this.AuthenticateBackOfficeAsync();
+
+ // if we are not authenticated then we need to redirect to the login page
+ if (!result.Succeeded)
+ {
+ RedirectToAction("Login", "Backoffice");
+ }
+
+ ViewResult defaultView = DefaultView();
+
+ return defaultView;
+ }
+
+ ///
+ /// Returns the default view for the BackOffice
+ ///
+ /// The default view currently /umbraco/UmbracoBackOffice/Default.cshtml
+ public ViewResult DefaultView()
+ {
+ var viewPath = Path.Combine(Constants.SystemDirectories.Umbraco, Constants.Web.Mvc.BackOfficeArea, nameof(Index) + ".cshtml")
+ .Replace("\\", "/"); // convert to forward slashes since it's a virtual path
+ return View(viewPath);
+ }
+}
diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/BackOfficeAuthBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/BackOfficeAuthBuilderExtensions.cs
index 3199cfbecc..0c303d2dc2 100644
--- a/src/Umbraco.Cms.Api.Management/DependencyInjection/BackOfficeAuthBuilderExtensions.cs
+++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/BackOfficeAuthBuilderExtensions.cs
@@ -19,13 +19,12 @@ public static class BackOfficeAuthBuilderExtensions
builder
.AddAuthentication()
.AddUmbracoOpenIddict()
- .AddBackOfficeLogin()
- .AddTokenRevocation();
+ .AddBackOfficeLogin();
return builder;
}
- private static IUmbracoBuilder AddTokenRevocation(this IUmbracoBuilder builder)
+ public static IUmbracoBuilder AddTokenRevocation(this IUmbracoBuilder builder)
{
builder.AddNotificationAsyncHandler();
builder.AddNotificationAsyncHandler();
@@ -52,28 +51,29 @@ public static class BackOfficeAuthBuilderExtensions
{
builder.Services
.AddAuthentication()
- .AddCookie(Constants.Security.NewBackOfficeAuthenticationType, options =>
+ // Add our custom schemes which are cookie handlers
+ .AddCookie(Constants.Security.BackOfficeAuthenticationType, options =>
{
options.LoginPath = "/umbraco/login";
- options.Cookie.Name = Constants.Security.NewBackOfficeAuthenticationType;
+ options.Cookie.Name = Constants.Security.BackOfficeAuthenticationType;
})
- .AddCookie(Constants.Security.NewBackOfficeExternalAuthenticationType, options =>
+ .AddCookie(Constants.Security.BackOfficeExternalAuthenticationType, o =>
{
- options.Cookie.Name = Constants.Security.NewBackOfficeExternalAuthenticationType;
- options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
+ o.Cookie.Name = Constants.Security.BackOfficeExternalAuthenticationType;
+ o.ExpireTimeSpan = TimeSpan.FromMinutes(5);
})
// Although we don't natively support this, we add it anyways so that if end-users implement the required logic
// they don't have to worry about manually adding this scheme or modifying the sign in manager
- .AddCookie(Constants.Security.NewBackOfficeTwoFactorAuthenticationType, options =>
+ .AddCookie(Constants.Security.BackOfficeTwoFactorAuthenticationType, options =>
{
- options.Cookie.Name = Constants.Security.NewBackOfficeTwoFactorAuthenticationType;
+ options.Cookie.Name = Constants.Security.BackOfficeTwoFactorAuthenticationType;
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
})
- .AddCookie(Constants.Security.NewBackOfficeTwoFactorRememberMeAuthenticationType, options =>
+ .AddCookie(Constants.Security.BackOfficeTwoFactorRememberMeAuthenticationType, o =>
{
- options.Cookie.Name = Constants.Security.NewBackOfficeTwoFactorRememberMeAuthenticationType;
- options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
+ o.Cookie.Name = Constants.Security.BackOfficeTwoFactorRememberMeAuthenticationType;
+ o.ExpireTimeSpan = TimeSpan.FromMinutes(5);
});
return builder;
diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/InstallerBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/InstallerBuilderExtensions.cs
index 2965e35b36..88d95b2fbd 100644
--- a/src/Umbraco.Cms.Api.Management/DependencyInjection/InstallerBuilderExtensions.cs
+++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/InstallerBuilderExtensions.cs
@@ -1,10 +1,12 @@
using Microsoft.Extensions.DependencyInjection;
+using Umbraco.Cms.Api.Management.Install;
using Umbraco.Cms.Api.Management.Mapping.Installer;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Factories;
using Umbraco.Cms.Core.Installer;
using Umbraco.Cms.Core.Installer.Steps;
using Umbraco.Cms.Core.Mapping;
+using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Services.Installer;
using Umbraco.Cms.Infrastructure.Factories.Installer;
using Umbraco.Cms.Infrastructure.Installer.Steps;
@@ -13,7 +15,7 @@ namespace Umbraco.Cms.Api.Management.DependencyInjection;
public static class InstallerBuilderExtensions
{
- internal static IUmbracoBuilder AddNewInstaller(this IUmbracoBuilder builder)
+ internal static IUmbracoBuilder AddInstaller(this IUmbracoBuilder builder)
{
IServiceCollection services = builder.Services;
@@ -23,7 +25,7 @@ public static class InstallerBuilderExtensions
builder.AddInstallSteps();
services.AddTransient();
-
+ builder.AddNotificationAsyncHandler();
builder.WithCollectionBuilder().Add();
return builder;
diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/PreviewBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/PreviewBuilderExtensions.cs
new file mode 100644
index 0000000000..81148fbaa9
--- /dev/null
+++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/PreviewBuilderExtensions.cs
@@ -0,0 +1,20 @@
+using Microsoft.Extensions.DependencyInjection;
+using Umbraco.Cms.Api.Management.Preview;
+using Umbraco.Cms.Api.Management.Routing;
+using Umbraco.Cms.Core.DependencyInjection;
+using Umbraco.Cms.Core.Notifications;
+
+namespace Umbraco.Cms.Api.Management.DependencyInjection;
+
+internal static class PreviewBuilderExtensions
+{
+ internal static IUmbracoBuilder AddPreview(this IUmbracoBuilder builder)
+ {
+ builder.Services.AddSignalR();
+ builder.Services.AddSingleton();
+ builder.AddNotificationAsyncHandler();
+
+ return builder;
+ }
+}
+
diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.BackOffice.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.BackOffice.cs
new file mode 100644
index 0000000000..20ee572dc4
--- /dev/null
+++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.BackOffice.cs
@@ -0,0 +1,61 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Umbraco.Cms.Api.Management.DependencyInjection;
+using Umbraco.Cms.Api.Management.Security;
+using Umbraco.Cms.Core.DependencyInjection;
+using Umbraco.Cms.Core.Hosting;
+using Umbraco.Cms.Core.IO;
+using Umbraco.Cms.Infrastructure.DependencyInjection;
+using Umbraco.Cms.Infrastructure.Examine.DependencyInjection;
+using Umbraco.Cms.Web.Common.Hosting;
+
+namespace Umbraco.Extensions;
+
+///
+/// Extension methods for for the Umbraco back office
+///
+public static partial class UmbracoBuilderExtensions
+{
+ ///
+ /// Adds all required components to run the Umbraco back office
+ ///
+ public static IUmbracoBuilder
+ AddBackOffice(this IUmbracoBuilder builder, Action? configureMvc = null) => builder
+ .AddConfiguration()
+ .AddUmbracoCore()
+ .AddWebComponents()
+ .AddRuntimeMinifier()
+ .AddBackOfficeCore()
+ .AddBackOfficeIdentity()
+ .AddBackOfficeAuthentication()
+ .AddTokenRevocation()
+ .AddMembersIdentity()
+ .AddUmbracoProfiler()
+ .AddMvcAndRazor(configureMvc)
+ .AddWebServer()
+ .AddRecurringBackgroundJobs()
+ .AddNuCache()
+ .AddDistributedCache()
+ .AddCoreNotifications()
+ .AddExamine()
+ .AddExamineIndexes();
+
+ public static IUmbracoBuilder AddBackOfficeCore(this IUmbracoBuilder builder)
+ {
+ builder.Services.AddUnique();
+ builder.Services.AddUnique(factory =>
+ {
+ var path = "~/";
+ IHostingEnvironment hostingEnvironment = factory.GetRequiredService();
+ return new PhysicalFileSystem(
+ factory.GetRequiredService(),
+ hostingEnvironment,
+ factory.GetRequiredService>(),
+ hostingEnvironment.MapPathContentRoot(path),
+ hostingEnvironment.ToAbsolute(path)
+ );
+ });
+
+ return builder;
+ }
+}
diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs
similarity index 92%
rename from src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs
rename to src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs
index 9f79b5e3bb..f3ae55c8f6 100644
--- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs
+++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs
@@ -3,6 +3,8 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using Umbraco.Cms.Api.Management.Security;
+using Umbraco.Cms.Api.Management.Telemetry;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
@@ -15,8 +17,6 @@ using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Infrastructure.Security;
using Umbraco.Cms.Infrastructure.Telemetry.Interfaces;
-using Umbraco.Cms.Web.BackOffice.Security;
-using Umbraco.Cms.Web.BackOffice.Telemetry;
using Umbraco.Cms.Web.Common.AspNetCore;
using Umbraco.Cms.Web.Common.Security;
@@ -63,18 +63,17 @@ public static partial class UmbracoBuilderExtensions
services.AddScoped();
services.AddScoped();
services.AddScoped();
+ services.AddScoped();
- services.AddSingleton(); ;
+ services.AddSingleton();
// Configure the options specifically for the UmbracoBackOfficeIdentityOptions instance
services.ConfigureOptions();
- services.ConfigureOptions();
return builder;
}
- //TODO change this to private when the legacy backoffice is removed
- public static BackOfficeIdentityBuilder BuildUmbracoBackOfficeIdentity(this IUmbracoBuilder builder)
+ private static BackOfficeIdentityBuilder BuildUmbracoBackOfficeIdentity(this IUmbracoBuilder builder)
{
IServiceCollection services = builder.Services;
diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.LocalizedText.cs
similarity index 100%
rename from src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs
rename to src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.LocalizedText.cs
diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilderExtensions.cs
index f36f88b384..718b2de9ed 100644
--- a/src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilderExtensions.cs
+++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilderExtensions.cs
@@ -3,25 +3,32 @@ using Umbraco.Cms.Api.Common.Configuration;
using Umbraco.Cms.Api.Common.DependencyInjection;
using Umbraco.Cms.Api.Management.Configuration;
using Umbraco.Cms.Api.Management.DependencyInjection;
+using Umbraco.Cms.Api.Management.Middleware;
+using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.Serialization;
using Umbraco.Cms.Api.Management.Services;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DependencyInjection;
+using Umbraco.Cms.Core.Services;
+using Umbraco.Cms.Web.BackOffice.Services;
using Umbraco.Cms.Web.Common.ApplicationBuilder;
namespace Umbraco.Extensions;
-public static class UmbracoBuilderExtensions
+public static partial class UmbracoBuilderExtensions
{
public static IUmbracoBuilder AddUmbracoManagementApi(this IUmbracoBuilder builder)
{
IServiceCollection services = builder.Services;
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+ builder.Services.AddUnique();
if (!services.Any(x => x.ImplementationType == typeof(JsonPatchService)))
{
ModelsBuilderBuilderExtensions.AddModelsBuilder(builder)
.AddJson()
- .AddNewInstaller()
+ .AddInstaller()
.AddUpgrader()
.AddSearchManagement()
.AddTrees()
@@ -55,8 +62,10 @@ public static class UmbracoBuilderExtensions
.AddWebhooks()
.AddServer()
.AddCorsPolicy()
- .AddBackOfficeAuthentication()
- .AddPasswordConfiguration();
+ .AddWebhooks()
+ .AddPreview()
+ .AddPasswordConfiguration()
+ .AddSupplemenataryLocalizedTextFileSources();
services
.ConfigureOptions()
diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/WebhookBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/WebhooksBuilderExtensions.cs
similarity index 63%
rename from src/Umbraco.Cms.Api.Management/DependencyInjection/WebhookBuilderExtensions.cs
rename to src/Umbraco.Cms.Api.Management/DependencyInjection/WebhooksBuilderExtensions.cs
index 85c85e3915..74cbe680a9 100644
--- a/src/Umbraco.Cms.Api.Management/DependencyInjection/WebhookBuilderExtensions.cs
+++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/WebhooksBuilderExtensions.cs
@@ -1,16 +1,13 @@
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Core.DependencyInjection;
-using Umbraco.Cms.Core.Mapping;
-using Umbraco.Cms.Web.BackOffice.Mapping;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Management.DependencyInjection;
-internal static class WebhookBuilderExtensions
+internal static class WebhooksBuilderExtensions
{
internal static IUmbracoBuilder AddWebhooks(this IUmbracoBuilder builder)
{
- builder.WithCollectionBuilder().Add();
builder.Services.AddUnique();
return builder;
diff --git a/src/Umbraco.Cms.Api.Management/Extensions/HtmlHelperBackOfficeExtensions.cs b/src/Umbraco.Cms.Api.Management/Extensions/HtmlHelperBackOfficeExtensions.cs
new file mode 100644
index 0000000000..f501b7f312
--- /dev/null
+++ b/src/Umbraco.Cms.Api.Management/Extensions/HtmlHelperBackOfficeExtensions.cs
@@ -0,0 +1,52 @@
+using System.Text;
+using Microsoft.AspNetCore.Html;
+using Microsoft.AspNetCore.Mvc.Rendering;
+using Umbraco.Cms.Core;
+using Umbraco.Cms.Core.Manifest;
+using Umbraco.Cms.Core.Serialization;
+using Umbraco.Cms.Web.Common.Hosting;
+
+namespace Umbraco.Cms.Api.Management.Extensions;
+
+public static class HtmlHelperBackOfficeExtensions
+{
+ ///
+ /// Outputs a script tag containing the import map for the BackOffice.
+ ///
+ ///
+ /// It will replace the token %CACHE_BUSTER% with the cache buster hash.
+ /// It will also replace the /umbraco/backoffice path with the correct path for the BackOffice assets.
+ ///
+ /// A containing the html content for the BackOffice import map.
+ public static async Task BackOfficeImportMapScriptAsync(
+ this IHtmlHelper html,
+ IJsonSerializer jsonSerializer,
+ IBackOfficePathGenerator backOfficePathGenerator,
+ IPackageManifestService packageManifestService)
+ {
+ try
+ {
+ PackageManifestImportmap packageImports = await packageManifestService.GetPackageManifestImportmapAsync();
+
+ var sb = new StringBuilder();
+ sb.AppendLine("""");
+
+ // Inject the BackOffice cache buster into the import string to handle BackOffice assets
+ var importmapScript = sb.ToString()
+ .Replace(backOfficePathGenerator.BackOfficeVirtualDirectory, backOfficePathGenerator.BackOfficeAssetsPath)
+ .Replace(Constants.Web.CacheBusterToken, backOfficePathGenerator.BackOfficeCacheBustHash);
+
+ return html.Raw(importmapScript);
+ }
+ catch (NotSupportedException ex)
+ {
+ throw new NotSupportedException("Failed to serialize the BackOffice import map", ex);
+ }
+ catch (Exception ex)
+ {
+ throw new Exception("Failed to generate the BackOffice import map", ex);
+ }
+ }
+}
diff --git a/src/Umbraco.Web.BackOffice/Extensions/HttpContextExtensions.cs b/src/Umbraco.Cms.Api.Management/Extensions/HttpContextExtensions.cs
similarity index 100%
rename from src/Umbraco.Web.BackOffice/Extensions/HttpContextExtensions.cs
rename to src/Umbraco.Cms.Api.Management/Extensions/HttpContextExtensions.cs
diff --git a/src/Umbraco.Web.BackOffice/Extensions/IdentityBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/Extensions/IdentityBuilderExtensions.cs
similarity index 100%
rename from src/Umbraco.Web.BackOffice/Extensions/IdentityBuilderExtensions.cs
rename to src/Umbraco.Cms.Api.Management/Extensions/IdentityBuilderExtensions.cs
diff --git a/src/Umbraco.Web.BackOffice/Extensions/UmbracoApplicationBuilder.BackOffice.cs b/src/Umbraco.Cms.Api.Management/Extensions/UmbracoApplicationBuilder.BackOffice.cs
similarity index 69%
rename from src/Umbraco.Web.BackOffice/Extensions/UmbracoApplicationBuilder.BackOffice.cs
rename to src/Umbraco.Cms.Api.Management/Extensions/UmbracoApplicationBuilder.BackOffice.cs
index 71e94a1b2d..b1e3000884 100644
--- a/src/Umbraco.Web.BackOffice/Extensions/UmbracoApplicationBuilder.BackOffice.cs
+++ b/src/Umbraco.Cms.Api.Management/Extensions/UmbracoApplicationBuilder.BackOffice.cs
@@ -1,10 +1,7 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
-using Umbraco.Cms.Core.Configuration.Models;
-using Umbraco.Cms.Core.Hosting;
-using Umbraco.Cms.Web.BackOffice.Middleware;
-using Umbraco.Cms.Web.BackOffice.Routing;
+using Umbraco.Cms.Api.Management.Middleware;
+using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Web.Common.ApplicationBuilder;
namespace Umbraco.Extensions;
@@ -21,13 +18,6 @@ public static partial class UmbracoApplicationBuilderExtensions
///
public static IUmbracoApplicationBuilderContext UseBackOffice(this IUmbracoApplicationBuilderContext builder)
{
- KeepAliveSettings keepAliveSettings =
- builder.ApplicationServices.GetRequiredService>().Value;
- IHostingEnvironment hostingEnvironment = builder.ApplicationServices.GetRequiredService();
- builder.AppBuilder.Map(
- hostingEnvironment.ToAbsolute(keepAliveSettings.KeepAlivePingUrl),
- a => a.UseMiddleware());
-
builder.AppBuilder.UseMiddleware();
return builder;
}
diff --git a/src/Umbraco.Web.BackOffice/Extensions/UmbracoApplicationBuilder.Preview.cs b/src/Umbraco.Cms.Api.Management/Extensions/UmbracoApplicationBuilder.Preview.cs
similarity index 93%
rename from src/Umbraco.Web.BackOffice/Extensions/UmbracoApplicationBuilder.Preview.cs
rename to src/Umbraco.Cms.Api.Management/Extensions/UmbracoApplicationBuilder.Preview.cs
index 18e1a877aa..a73ba3c492 100644
--- a/src/Umbraco.Web.BackOffice/Extensions/UmbracoApplicationBuilder.Preview.cs
+++ b/src/Umbraco.Cms.Api.Management/Extensions/UmbracoApplicationBuilder.Preview.cs
@@ -1,5 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
-using Umbraco.Cms.Web.BackOffice.Routing;
+using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Web.Common.ApplicationBuilder;
namespace Umbraco.Extensions;
diff --git a/src/Umbraco.Cms.Api.Management/Factories/IWebhookPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/IWebhookPresentationFactory.cs
index 113d739bfb..722d909cb6 100644
--- a/src/Umbraco.Cms.Api.Management/Factories/IWebhookPresentationFactory.cs
+++ b/src/Umbraco.Cms.Api.Management/Factories/IWebhookPresentationFactory.cs
@@ -1,5 +1,6 @@
-using Umbraco.Cms.Api.Management.ViewModels.Webhook;
+using Umbraco.Cms.Api.Management.ViewModels.Webhook;
using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Web.Common.Models;
namespace Umbraco.Cms.Api.Management.Factories;
diff --git a/src/Umbraco.Web.BackOffice/Install/CreateUnattendedUserNotificationHandler.cs b/src/Umbraco.Cms.Api.Management/Install/CreateUnattendedUserNotificationHandler.cs
similarity index 95%
rename from src/Umbraco.Web.BackOffice/Install/CreateUnattendedUserNotificationHandler.cs
rename to src/Umbraco.Cms.Api.Management/Install/CreateUnattendedUserNotificationHandler.cs
index 11648a2618..a1ee165da7 100644
--- a/src/Umbraco.Web.BackOffice/Install/CreateUnattendedUserNotificationHandler.cs
+++ b/src/Umbraco.Cms.Api.Management/Install/CreateUnattendedUserNotificationHandler.cs
@@ -1,4 +1,4 @@
-using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core;
@@ -10,7 +10,7 @@ using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Extensions;
-namespace Umbraco.Cms.Web.BackOffice.Install;
+namespace Umbraco.Cms.Api.Management.Install;
public class CreateUnattendedUserNotificationHandler : INotificationAsyncHandler
{
@@ -18,8 +18,7 @@ public class CreateUnattendedUserNotificationHandler : INotificationAsyncHandler
private readonly IOptions _unattendedSettings;
private readonly IUserService _userService;
- public CreateUnattendedUserNotificationHandler(IOptions unattendedSettings,
- IUserService userService, IServiceScopeFactory serviceScopeFactory)
+ public CreateUnattendedUserNotificationHandler(IOptions unattendedSettings, IUserService userService, IServiceScopeFactory serviceScopeFactory)
{
_unattendedSettings = unattendedSettings;
_userService = userService;
diff --git a/src/Umbraco.Cms.Api.Management/ManagementApiComposer.cs b/src/Umbraco.Cms.Api.Management/ManagementApiComposer.cs
index 77f2740c1e..5e8bff68c1 100644
--- a/src/Umbraco.Cms.Api.Management/ManagementApiComposer.cs
+++ b/src/Umbraco.Cms.Api.Management/ManagementApiComposer.cs
@@ -6,6 +6,7 @@ namespace Umbraco.Cms.Api.Management;
public class ManagementApiComposer : IComposer
{
- public void Compose(IUmbracoBuilder builder) => builder.AddUmbracoManagementApi();
+ public void Compose(IUmbracoBuilder builder) =>
+ builder.AddUmbracoManagementApi();
}
diff --git a/src/Umbraco.Cms.Api.Management/Mapping/Webhook/WebhookMapDefinition.cs b/src/Umbraco.Cms.Api.Management/Mapping/Webhook/WebhookMapDefinition.cs
deleted file mode 100644
index cc89a5b27f..0000000000
--- a/src/Umbraco.Cms.Api.Management/Mapping/Webhook/WebhookMapDefinition.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using Umbraco.Cms.Api.Management.ViewModels.Webhook;
-using Umbraco.Cms.Core.Hosting;
-using Umbraco.Cms.Core.Mapping;
-using Umbraco.Cms.Core.Models;
-using Umbraco.Cms.Core.Services;
-using Umbraco.Cms.Core.Webhooks;
-using Umbraco.Cms.Web.Common.Models;
-
-namespace Umbraco.Cms.Web.BackOffice.Mapping;
-
-public class WebhookMapDefinition : IMapDefinition
-{
- private readonly IHostingEnvironment _hostingEnvironment;
- private readonly ILocalizedTextService _localizedTextService;
-
- public WebhookMapDefinition(IHostingEnvironment hostingEnvironment, ILocalizedTextService localizedTextService)
- {
- _hostingEnvironment = hostingEnvironment;
- _localizedTextService = localizedTextService;
- }
-
- public void DefineMaps(IUmbracoMapper mapper)
- {
- mapper.Define((_, _) => new WebhookEventResponseModel(), Map);
- mapper.Define((_, _) => new WebhookLogViewModel(), Map);
- mapper.Define((_, _) => new Webhook(string.Empty), Map);
- mapper.Define((_, _) => new Webhook(string.Empty), Map);
- }
-
- // Umbraco.Code.MapAll
- private void Map(IWebhookEvent source, WebhookEventResponseModel target, MapperContext context)
- {
- target.EventName = source.EventName;
- target.EventType = source.EventType;
- target.Alias = source.Alias;
- }
-
- // Umbraco.Code.MapAll -CreateDate -DeleteDate -Id -UpdateDate
- private void Map(CreateWebhookRequestModel source, IWebhook target, MapperContext context)
- {
- target.Url = source.Url;
- target.Enabled = source.Enabled;
- target.ContentTypeKeys = source.ContentTypeKeys;
- target.Events = source.Events;
- target.Headers = source.Headers;
- target.Key = source.Id ?? Guid.NewGuid();
- }
-
- // Umbraco.Code.MapAll -CreateDate -DeleteDate -Id -UpdateDate -Key
- private void Map(UpdateWebhookRequestModel source, IWebhook target, MapperContext context)
- {
- target.Url = source.Url;
- target.Enabled = source.Enabled;
- target.ContentTypeKeys = source.ContentTypeKeys;
- target.Events = source.Events;
- target.Headers = source.Headers;
- }
-
- // Umbraco.Code.MapAll
- private void Map(WebhookLog source, WebhookLogViewModel target, MapperContext context)
- {
- target.Date = source.Date;
- target.EventAlias = source.EventAlias;
- target.Key = source.Key;
- target.RequestBody = source.RequestBody ?? string.Empty;
- target.RetryCount = source.RetryCount;
- target.Url = source.Url;
- target.RequestHeaders = source.RequestHeaders;
- target.WebhookKey = source.WebhookKey;
-
- if (_hostingEnvironment.IsDebugMode)
- {
- target.ExceptionOccured = source.ExceptionOccured;
- target.ResponseBody = source.ResponseBody;
- target.ResponseHeaders = source.ResponseHeaders;
- target.StatusCode = source.StatusCode;
- }
- else
- {
- target.ResponseBody = _localizedTextService.Localize("webhooks", "toggleDebug", Thread.CurrentThread.CurrentUICulture);
- target.StatusCode = source.StatusCode is "OK (200)" ? source.StatusCode : _localizedTextService.Localize("webhooks", "statusNotOk", Thread.CurrentThread.CurrentUICulture);
- }
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Middleware/BackOfficeExternalLoginProviderErrorMiddleware.cs b/src/Umbraco.Cms.Api.Management/Middleware/BackOfficeExternalLoginProviderErrorMiddleware.cs
similarity index 97%
rename from src/Umbraco.Web.BackOffice/Middleware/BackOfficeExternalLoginProviderErrorMiddleware.cs
rename to src/Umbraco.Cms.Api.Management/Middleware/BackOfficeExternalLoginProviderErrorMiddleware.cs
index 9dbe09e119..95128b47f7 100644
--- a/src/Umbraco.Web.BackOffice/Middleware/BackOfficeExternalLoginProviderErrorMiddleware.cs
+++ b/src/Umbraco.Cms.Api.Management/Middleware/BackOfficeExternalLoginProviderErrorMiddleware.cs
@@ -5,7 +5,7 @@ using Newtonsoft.Json;
using Umbraco.Cms.Core.Security;
using Umbraco.Extensions;
-namespace Umbraco.Cms.Web.BackOffice.Middleware;
+namespace Umbraco.Cms.Api.Management.Middleware;
///
/// Used to handle errors registered by external login providers
diff --git a/src/Umbraco.Web.BackOffice/Filters/UnhandledExceptionLoggerMiddleware.cs b/src/Umbraco.Cms.Api.Management/Middleware/UnhandledExceptionLoggerMiddleware.cs
similarity index 93%
rename from src/Umbraco.Web.BackOffice/Filters/UnhandledExceptionLoggerMiddleware.cs
rename to src/Umbraco.Cms.Api.Management/Middleware/UnhandledExceptionLoggerMiddleware.cs
index 06eaf3fd30..c991937e65 100644
--- a/src/Umbraco.Web.BackOffice/Filters/UnhandledExceptionLoggerMiddleware.cs
+++ b/src/Umbraco.Cms.Api.Management/Middleware/UnhandledExceptionLoggerMiddleware.cs
@@ -1,9 +1,9 @@
-using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Logging;
using Umbraco.Extensions;
-namespace Umbraco.Cms.Web.BackOffice.Filters;
+namespace Umbraco.Cms.Api.Management.Middleware;
///
/// Logs any unhandled exception.
diff --git a/src/Umbraco.Web.BackOffice/SignalR/IPreviewHub.cs b/src/Umbraco.Cms.Api.Management/Preview/IPreviewHub.cs
similarity index 80%
rename from src/Umbraco.Web.BackOffice/SignalR/IPreviewHub.cs
rename to src/Umbraco.Cms.Api.Management/Preview/IPreviewHub.cs
index 4ba4b9fd26..fe04c028ee 100644
--- a/src/Umbraco.Web.BackOffice/SignalR/IPreviewHub.cs
+++ b/src/Umbraco.Cms.Api.Management/Preview/IPreviewHub.cs
@@ -1,4 +1,4 @@
-namespace Umbraco.Cms.Web.BackOffice.SignalR;
+namespace Umbraco.Cms.Api.Management.Preview;
public interface IPreviewHub
{
diff --git a/src/Umbraco.Web.BackOffice/SignalR/PreviewHub.cs b/src/Umbraco.Cms.Api.Management/Preview/PreviewHub.cs
similarity index 64%
rename from src/Umbraco.Web.BackOffice/SignalR/PreviewHub.cs
rename to src/Umbraco.Cms.Api.Management/Preview/PreviewHub.cs
index b5407f385c..d5cb5e4e55 100644
--- a/src/Umbraco.Web.BackOffice/SignalR/PreviewHub.cs
+++ b/src/Umbraco.Cms.Api.Management/Preview/PreviewHub.cs
@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.SignalR;
-namespace Umbraco.Cms.Web.BackOffice.SignalR;
+namespace Umbraco.Cms.Api.Management.Preview;
public class PreviewHub : Hub
{
diff --git a/src/Umbraco.Web.BackOffice/SignalR/PreviewHubUpdater.cs b/src/Umbraco.Cms.Api.Management/Preview/PreviewHubUpdater.cs
similarity index 96%
rename from src/Umbraco.Web.BackOffice/SignalR/PreviewHubUpdater.cs
rename to src/Umbraco.Cms.Api.Management/Preview/PreviewHubUpdater.cs
index 104deceba2..f88c61ac4a 100644
--- a/src/Umbraco.Web.BackOffice/SignalR/PreviewHubUpdater.cs
+++ b/src/Umbraco.Cms.Api.Management/Preview/PreviewHubUpdater.cs
@@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Sync;
-namespace Umbraco.Cms.Web.BackOffice.SignalR;
+namespace Umbraco.Cms.Api.Management.Preview;
public class PreviewHubUpdater : INotificationAsyncHandler
{
diff --git a/src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs b/src/Umbraco.Cms.Api.Management/Routing/BackOfficeAreaRoutes.cs
similarity index 87%
rename from src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs
rename to src/Umbraco.Cms.Api.Management/Routing/BackOfficeAreaRoutes.cs
index 24a90fa0f1..6e725d4191 100644
--- a/src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs
+++ b/src/Umbraco.Cms.Api.Management/Routing/BackOfficeAreaRoutes.cs
@@ -1,19 +1,18 @@
using System.Text;
using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Options;
+using Umbraco.Cms.Api.Management.Controllers.Security;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Web.Mvc;
-using Umbraco.Cms.Web.BackOffice.Controllers;
using Umbraco.Cms.Web.Common.Controllers;
using Umbraco.Cms.Web.Common.Routing;
using Umbraco.Extensions;
-namespace Umbraco.Cms.Web.BackOffice.Routing;
+namespace Umbraco.Cms.Api.Management.Routing;
///
/// Creates routes for the back office area
@@ -68,27 +67,24 @@ public sealed class BackOfficeAreaRoutes : IAreaRoutes
///
private void MapMinimalBackOffice(IEndpointRouteBuilder endpoints)
{
- endpoints.MapUmbracoRoute(
+ endpoints.MapUmbracoRoute(
_umbracoPathSegment,
- Constants.Web.Mvc.BackOfficeArea,
+ null!,
string.Empty,
- "Default",
+ "Index",
false,
// Limit the action/id to only allow characters - this is so this route doesn't hog all other
// routes like: /umbraco/channels/word.aspx, etc...
// (Not that we have to worry about too many of those these days, there still might be a need for these constraints).
new { action = @"[a-zA-Z]*", id = @"[a-zA-Z]*" });
- endpoints.MapUmbracoApiRoute(_umbracoPathSegment, Constants.Web.Mvc.BackOfficeApiArea, true, string.Empty);
-
- endpoints.MapAreaControllerRoute(
+ endpoints.MapControllerRoute(
"catch-all-sections-to-client",
- Constants.Web.Mvc.BackOfficeArea,
new StringBuilder(_umbracoPathSegment).Append("/section/{**slug}").ToString(),
new
{
- Controller = ControllerExtensions.GetControllerName(),
- Action = nameof(BackOfficeController.Default)
+ Controller = ControllerExtensions.GetControllerName(),
+ Action = nameof(BackOfficeDefaultController.Index)
});
}
diff --git a/src/Umbraco.Web.BackOffice/Routing/PreviewRoutes.cs b/src/Umbraco.Cms.Api.Management/Routing/PreviewRoutes.cs
similarity index 84%
rename from src/Umbraco.Web.BackOffice/Routing/PreviewRoutes.cs
rename to src/Umbraco.Cms.Api.Management/Routing/PreviewRoutes.cs
index d91a67f9f0..6f19010775 100644
--- a/src/Umbraco.Web.BackOffice/Routing/PreviewRoutes.cs
+++ b/src/Umbraco.Cms.Api.Management/Routing/PreviewRoutes.cs
@@ -1,16 +1,15 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Options;
+using Umbraco.Cms.Api.Management.Preview;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Services;
-using Umbraco.Cms.Web.BackOffice.Controllers;
-using Umbraco.Cms.Web.BackOffice.SignalR;
using Umbraco.Cms.Web.Common.Routing;
using Umbraco.Extensions;
-namespace Umbraco.Cms.Web.BackOffice.Routing;
+namespace Umbraco.Cms.Api.Management.Routing;
///
/// Creates routes for the preview hub
@@ -37,8 +36,6 @@ public sealed class PreviewRoutes : IAreaRoutes
case RuntimeLevel.Upgrade:
case RuntimeLevel.Run:
endpoints.MapHub(GetPreviewHubRoute());
- endpoints.MapUmbracoRoute(_umbracoPathSegment, Constants.Web.Mvc.BackOfficeArea,
- null);
break;
case RuntimeLevel.BootFailed:
case RuntimeLevel.Unknown:
diff --git a/src/Umbraco.Web.BackOffice/Security/AutoLinkSignInResult.cs b/src/Umbraco.Cms.Api.Management/Security/AutoLinkSignInResult.cs
similarity index 95%
rename from src/Umbraco.Web.BackOffice/Security/AutoLinkSignInResult.cs
rename to src/Umbraco.Cms.Api.Management/Security/AutoLinkSignInResult.cs
index 86a8a71c76..14995bf288 100644
--- a/src/Umbraco.Web.BackOffice/Security/AutoLinkSignInResult.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/AutoLinkSignInResult.cs
@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Identity;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
/// Result returned from signing in when auto-linking takes place
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs b/src/Umbraco.Cms.Api.Management/Security/BackOfficeAuthenticationBuilder.cs
similarity index 98%
rename from src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs
rename to src/Umbraco.Cms.Api.Management/Security/BackOfficeAuthenticationBuilder.cs
index f4aed22fbe..f688d138bd 100644
--- a/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/BackOfficeAuthenticationBuilder.cs
@@ -5,7 +5,7 @@ using Microsoft.Extensions.Options;
using Umbraco.Cms.Core;
using Umbraco.Extensions;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
/// Custom used to associate external logins with umbraco external login options
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternaLoginProviderScheme.cs b/src/Umbraco.Cms.Api.Management/Security/BackOfficeExternaLoginProviderScheme.cs
similarity index 93%
rename from src/Umbraco.Web.BackOffice/Security/BackOfficeExternaLoginProviderScheme.cs
rename to src/Umbraco.Cms.Api.Management/Security/BackOfficeExternaLoginProviderScheme.cs
index 322d81c550..e716f0b748 100644
--- a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternaLoginProviderScheme.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/BackOfficeExternaLoginProviderScheme.cs
@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Authentication;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
public class BackOfficeExternaLoginProviderScheme
{
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProvider.cs b/src/Umbraco.Cms.Api.Management/Security/BackOfficeExternalLoginProvider.cs
similarity index 96%
rename from src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProvider.cs
rename to src/Umbraco.Cms.Api.Management/Security/BackOfficeExternalLoginProvider.cs
index eeb68fd19a..85663f683b 100644
--- a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProvider.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/BackOfficeExternalLoginProvider.cs
@@ -1,6 +1,6 @@
using Microsoft.Extensions.Options;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
/// An external login (OAuth) provider for the back office
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs b/src/Umbraco.Cms.Api.Management/Security/BackOfficeExternalLoginProviderOptions.cs
similarity index 99%
rename from src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs
rename to src/Umbraco.Cms.Api.Management/Security/BackOfficeExternalLoginProviderOptions.cs
index 32261eefef..19a74757f8 100644
--- a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/BackOfficeExternalLoginProviderOptions.cs
@@ -1,6 +1,6 @@
using Umbraco.Cms.Core.Models;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
/// Options used to configure back office external login providers
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviders.cs b/src/Umbraco.Cms.Api.Management/Security/BackOfficeExternalLoginProviders.cs
similarity index 98%
rename from src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviders.cs
rename to src/Umbraco.Cms.Api.Management/Security/BackOfficeExternalLoginProviders.cs
index 75548032f4..ee620864f5 100644
--- a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviders.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/BackOfficeExternalLoginProviders.cs
@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Authentication;
using Umbraco.Cms.Core.Security;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
public class BackOfficeExternalLoginProviders : IBackOfficeExternalLoginProviders, ILocalLoginSettingProvider
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginsBuilder.cs b/src/Umbraco.Cms.Api.Management/Security/BackOfficeExternalLoginsBuilder.cs
similarity index 94%
rename from src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginsBuilder.cs
rename to src/Umbraco.Cms.Api.Management/Security/BackOfficeExternalLoginsBuilder.cs
index 4a15be1d54..a70a062d05 100644
--- a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginsBuilder.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/BackOfficeExternalLoginsBuilder.cs
@@ -1,6 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
/// Used to add back office login providers
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficePasswordChanger.cs b/src/Umbraco.Cms.Api.Management/Security/BackOfficePasswordChanger.cs
similarity index 92%
rename from src/Umbraco.Web.BackOffice/Security/BackOfficePasswordChanger.cs
rename to src/Umbraco.Cms.Api.Management/Security/BackOfficePasswordChanger.cs
index d4c3bfe83b..60983804f2 100644
--- a/src/Umbraco.Web.BackOffice/Security/BackOfficePasswordChanger.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/BackOfficePasswordChanger.cs
@@ -2,9 +2,8 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Security;
-using Umbraco.Cms.Web.Common.Security;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
public class BackOfficePasswordChanger : IBackOfficePasswordChanger
{
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeSecureDataFormat.cs b/src/Umbraco.Cms.Api.Management/Security/BackOfficeSecureDataFormat.cs
similarity index 98%
rename from src/Umbraco.Web.BackOffice/Security/BackOfficeSecureDataFormat.cs
rename to src/Umbraco.Cms.Api.Management/Security/BackOfficeSecureDataFormat.cs
index a30a13722c..d2e0e799a8 100644
--- a/src/Umbraco.Web.BackOffice/Security/BackOfficeSecureDataFormat.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/BackOfficeSecureDataFormat.cs
@@ -2,7 +2,7 @@ using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Umbraco.Extensions;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
/// Custom secure format that ensures the Identity in the ticket is verified
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs b/src/Umbraco.Cms.Api.Management/Security/BackOfficeSignInManager.cs
similarity index 99%
rename from src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs
rename to src/Umbraco.Cms.Api.Management/Security/BackOfficeSignInManager.cs
index 8cbb4adb3e..e5c36cf77c 100644
--- a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/BackOfficeSignInManager.cs
@@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
-using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
@@ -15,7 +14,7 @@ using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Web.Common.Security;
using Umbraco.Extensions;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
/// The sign in manager for back office users
diff --git a/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeIdentityOptions.cs b/src/Umbraco.Cms.Api.Management/Security/ConfigureBackOfficeIdentityOptions.cs
similarity index 84%
rename from src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeIdentityOptions.cs
rename to src/Umbraco.Cms.Api.Management/Security/ConfigureBackOfficeIdentityOptions.cs
index 211528afab..f5e85fe6e5 100644
--- a/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeIdentityOptions.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/ConfigureBackOfficeIdentityOptions.cs
@@ -7,7 +7,7 @@ using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Web.Common.DependencyInjection;
using Umbraco.Extensions;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
/// Used to configure for the Umbraco Back office
@@ -17,12 +17,6 @@ public sealed class ConfigureBackOfficeIdentityOptions : IConfigureOptions userPasswordConfiguration)
- : this(userPasswordConfiguration, StaticServiceProvider.Instance.GetRequiredService>())
- {
- }
-
public ConfigureBackOfficeIdentityOptions(
IOptions userPasswordConfiguration,
IOptions securitySettings)
diff --git a/src/Umbraco.Web.BackOffice/Security/DefaultBackOfficeTwoFactorOptions.cs b/src/Umbraco.Cms.Api.Management/Security/DefaultBackOfficeTwoFactorOptions.cs
similarity index 91%
rename from src/Umbraco.Web.BackOffice/Security/DefaultBackOfficeTwoFactorOptions.cs
rename to src/Umbraco.Cms.Api.Management/Security/DefaultBackOfficeTwoFactorOptions.cs
index 7eeb51077b..53f9cac9da 100644
--- a/src/Umbraco.Web.BackOffice/Security/DefaultBackOfficeTwoFactorOptions.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/DefaultBackOfficeTwoFactorOptions.cs
@@ -1,4 +1,4 @@
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
public class DefaultBackOfficeTwoFactorOptions : IBackOfficeTwoFactorOptions
diff --git a/src/Umbraco.Web.BackOffice/Security/ExternalLoginSignInResult.cs b/src/Umbraco.Cms.Api.Management/Security/ExternalLoginSignInResult.cs
similarity index 86%
rename from src/Umbraco.Web.BackOffice/Security/ExternalLoginSignInResult.cs
rename to src/Umbraco.Cms.Api.Management/Security/ExternalLoginSignInResult.cs
index e35d4e632e..b543c45204 100644
--- a/src/Umbraco.Web.BackOffice/Security/ExternalLoginSignInResult.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/ExternalLoginSignInResult.cs
@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Identity;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
/// Result returned from signing in when external logins are used.
diff --git a/src/Umbraco.Web.BackOffice/Security/ExternalSignInAutoLinkOptions.cs b/src/Umbraco.Cms.Api.Management/Security/ExternalSignInAutoLinkOptions.cs
similarity index 98%
rename from src/Umbraco.Web.BackOffice/Security/ExternalSignInAutoLinkOptions.cs
rename to src/Umbraco.Cms.Api.Management/Security/ExternalSignInAutoLinkOptions.cs
index bbf37ed8bb..c8fc4d6107 100644
--- a/src/Umbraco.Web.BackOffice/Security/ExternalSignInAutoLinkOptions.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/ExternalSignInAutoLinkOptions.cs
@@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Security;
using SecurityConstants = Umbraco.Cms.Core.Constants.Security;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
/// Options used to configure auto-linking external OAuth providers
diff --git a/src/Umbraco.Web.BackOffice/Security/ForgotPasswordUriProvider.cs b/src/Umbraco.Cms.Api.Management/Security/ForgotPasswordUriProvider.cs
similarity index 83%
rename from src/Umbraco.Web.BackOffice/Security/ForgotPasswordUriProvider.cs
rename to src/Umbraco.Cms.Api.Management/Security/ForgotPasswordUriProvider.cs
index f44420672e..0c43933014 100644
--- a/src/Umbraco.Web.BackOffice/Security/ForgotPasswordUriProvider.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/ForgotPasswordUriProvider.cs
@@ -7,10 +7,9 @@ using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services.OperationStatus;
-using Umbraco.Cms.Web.BackOffice.Controllers;
using Umbraco.Extensions;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
public class ForgotPasswordUriProvider : IForgotPasswordUriProvider
{
@@ -43,10 +42,11 @@ public class ForgotPasswordUriProvider : IForgotPasswordUriProvider
string forgotPasswordToken = $"{user.Key}{WebUtility.UrlEncode("|")}{tokenAttempt.Result.ToUrlBase64()}";
// FIXME: This will need to change.
- string? action = _linkGenerator.GetPathByAction(
- nameof(BackOfficeController.ValidatePasswordResetCode),
- ControllerExtensions.GetControllerName(),
- new { area = Constants.Web.Mvc.BackOfficeArea, invite = forgotPasswordToken });
+ // string? action = _linkGenerator.GetPathByAction(
+ // nameof(BackOfficeController.ValidatePasswordResetCode),
+ // ControllerExtensions.GetControllerName(),
+ // new { area = Constants.Web.Mvc.BackOfficeArea, invite = forgotPasswordToken });
+ string action = string.Empty;
Uri applicationUri = _httpContextAccessor
.GetRequiredHttpContext()
diff --git a/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs b/src/Umbraco.Cms.Api.Management/Security/IBackOfficeExternalLoginProviders.cs
similarity index 96%
rename from src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs
rename to src/Umbraco.Cms.Api.Management/Security/IBackOfficeExternalLoginProviders.cs
index 6d0a699f9a..288ea3e741 100644
--- a/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/IBackOfficeExternalLoginProviders.cs
@@ -1,4 +1,4 @@
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
/// Service to return instances
diff --git a/src/Umbraco.Web.BackOffice/Security/IBackOfficeTwoFactorOptions.cs b/src/Umbraco.Cms.Api.Management/Security/IBackOfficeTwoFactorOptions.cs
similarity index 90%
rename from src/Umbraco.Web.BackOffice/Security/IBackOfficeTwoFactorOptions.cs
rename to src/Umbraco.Cms.Api.Management/Security/IBackOfficeTwoFactorOptions.cs
index 792fbf237a..748eee41f8 100644
--- a/src/Umbraco.Web.BackOffice/Security/IBackOfficeTwoFactorOptions.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/IBackOfficeTwoFactorOptions.cs
@@ -1,4 +1,4 @@
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
/// Options used to control 2FA for the Umbraco back office.
diff --git a/src/Umbraco.Web.BackOffice/Security/InviteUriProvider.cs b/src/Umbraco.Cms.Api.Management/Security/InviteUriProvider.cs
similarity index 83%
rename from src/Umbraco.Web.BackOffice/Security/InviteUriProvider.cs
rename to src/Umbraco.Cms.Api.Management/Security/InviteUriProvider.cs
index 8226848604..b5b9c51cab 100644
--- a/src/Umbraco.Web.BackOffice/Security/InviteUriProvider.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/InviteUriProvider.cs
@@ -7,10 +7,9 @@ using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services.OperationStatus;
-using Umbraco.Cms.Web.BackOffice.Controllers;
using Umbraco.Extensions;
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
public class InviteUriProvider : IInviteUriProvider
{
@@ -43,10 +42,11 @@ public class InviteUriProvider : IInviteUriProvider
string inviteToken = $"{invitee.Key}{WebUtility.UrlEncode("|")}{tokenAttempt.Result.ToUrlBase64()}";
// FIXME: This will need to change.
- string? action = _linkGenerator.GetPathByAction(
- nameof(BackOfficeController.VerifyInvite),
- ControllerExtensions.GetControllerName(),
- new { area = Constants.Web.Mvc.BackOfficeArea, invite = inviteToken });
+ // string? action = _linkGenerator.GetPathByAction(
+ // nameof(BackOfficeController.VerifyInvite),
+ // ControllerExtensions.GetControllerName(),
+ // new { area = Constants.Web.Mvc.BackOfficeArea, invite = inviteToken });
+ string action = string.Empty;
Uri applicationUri = _httpContextAccessor
.GetRequiredHttpContext()
diff --git a/src/Umbraco.Web.BackOffice/Security/TwoFactorLoginViewOptions.cs b/src/Umbraco.Cms.Api.Management/Security/TwoFactorLoginViewOptions.cs
similarity index 86%
rename from src/Umbraco.Web.BackOffice/Security/TwoFactorLoginViewOptions.cs
rename to src/Umbraco.Cms.Api.Management/Security/TwoFactorLoginViewOptions.cs
index b5487b249e..44cc9db3a7 100644
--- a/src/Umbraco.Web.BackOffice/Security/TwoFactorLoginViewOptions.cs
+++ b/src/Umbraco.Cms.Api.Management/Security/TwoFactorLoginViewOptions.cs
@@ -1,4 +1,4 @@
-namespace Umbraco.Cms.Web.BackOffice.Security;
+namespace Umbraco.Cms.Api.Management.Security;
///
/// Options used as named options for 2fa providers
diff --git a/src/Umbraco.Web.BackOffice/Services/ConflictingRouteService.cs b/src/Umbraco.Cms.Api.Management/Services/ConflictingRouteService.cs
similarity index 91%
rename from src/Umbraco.Web.BackOffice/Services/ConflictingRouteService.cs
rename to src/Umbraco.Cms.Api.Management/Services/ConflictingRouteService.cs
index 86bc607edb..322096c89b 100644
--- a/src/Umbraco.Web.BackOffice/Services/ConflictingRouteService.cs
+++ b/src/Umbraco.Cms.Api.Management/Services/ConflictingRouteService.cs
@@ -1,8 +1,8 @@
using System.Reflection;
+using Umbraco.Cms.Api.Management.Controllers;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.Common.Attributes;
-using Umbraco.Cms.Web.Common.Controllers;
namespace Umbraco.Cms.Web.BackOffice.Services;
@@ -18,7 +18,7 @@ public class ConflictingRouteService : IConflictingRouteService
///
public bool HasConflictingRoutes(out string controllerName)
{
- var controllers = _typeLoader.GetTypes().ToList();
+ var controllers = _typeLoader.GetTypes().ToList();
foreach (Type controller in controllers)
{
Type[] potentialConflicting = controllers.Where(x => x.Name == controller.Name).ToArray();
diff --git a/src/Umbraco.Web.BackOffice/Telemetry/ExternalLoginTelemetryProvider.cs b/src/Umbraco.Cms.Api.Management/Telemetry/ExternalLoginTelemetryProvider.cs
similarity index 76%
rename from src/Umbraco.Web.BackOffice/Telemetry/ExternalLoginTelemetryProvider.cs
rename to src/Umbraco.Cms.Api.Management/Telemetry/ExternalLoginTelemetryProvider.cs
index 21a59796b3..6778ad6de3 100644
--- a/src/Umbraco.Web.BackOffice/Telemetry/ExternalLoginTelemetryProvider.cs
+++ b/src/Umbraco.Cms.Api.Management/Telemetry/ExternalLoginTelemetryProvider.cs
@@ -1,13 +1,14 @@
-using Umbraco.Cms.Core;
+using Umbraco.Cms.Api.Management.Security;
+using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Infrastructure.Telemetry.Interfaces;
-using Umbraco.Cms.Web.BackOffice.Security;
-namespace Umbraco.Cms.Web.BackOffice.Telemetry;
+namespace Umbraco.Cms.Api.Management.Telemetry;
public class ExternalLoginTelemetryProvider : IDetailedTelemetryProvider
{
- private readonly IBackOfficeExternalLoginProviders _externalLoginProviders;
+ private readonly
+ IBackOfficeExternalLoginProviders _externalLoginProviders;
public ExternalLoginTelemetryProvider(IBackOfficeExternalLoginProviders externalLoginProviders)
{
diff --git a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj
index 1ff599251b..58d8a0e4e9 100644
--- a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj
+++ b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj
@@ -9,23 +9,16 @@
- $(ProjectDir)wwwroot\umbraco
- $(BasePath)\lib$(BasePath)\backoffice$(BasePath)\login
-
-
-
-
-
@@ -37,11 +30,6 @@
-
-
-
-
-
@@ -54,10 +42,6 @@
-
-
-
-
@@ -70,13 +54,6 @@
-
-
-
-
-
-
-
diff --git a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/AuthorizeUpgrade.cshtml b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/AuthorizeUpgrade.cshtml
deleted file mode 100644
index ab45a61ed0..0000000000
--- a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/AuthorizeUpgrade.cshtml
+++ /dev/null
@@ -1,73 +0,0 @@
-@using Microsoft.Extensions.Options;
-@using Umbraco.Cms.Core
-@using Umbraco.Cms.Core.Configuration
-@using Umbraco.Cms.Core.Configuration.Models
-@using Umbraco.Cms.Core.Hosting
-@using Umbraco.Cms.Core.WebAssets
-@using Umbraco.Cms.Infrastructure.WebAssets
-@using Umbraco.Cms.Web.BackOffice.Controllers
-@using Umbraco.Cms.Web.BackOffice.Security
-@using Umbraco.Extensions
-@inject BackOfficeServerVariables backOfficeServerVariables
-@inject IUmbracoVersion umbracoVersion
-@inject IHostingEnvironment hostingEnvironment
-@inject IOptions globalSettings
-@inject IBackOfficeExternalLoginProviders externalLogins
-@inject IRuntimeMinifier runtimeMinifier
-
-@{
- var backOfficePath = globalSettings.Value.GetBackOfficePath(hostingEnvironment);
-}
-
-
-
-
-
-
-
-
-
- Umbraco
-
- @Html.Raw(await runtimeMinifier.RenderCssHereAsync(BackOfficeWebAssets.UmbracoUpgradeCssBundleName))
-
- @*Because we're lazy loading angular js, the embedded cloak style will not be loaded initially, but we need it*@
-
-
-
-
-
-
-
-
-
- @{
- var externalLoginUrl = Url.Action("ExternalLogin", "BackOffice", new
- {
- area = ViewData.GetUmbracoPath(),
- //Custom redirect URL since we don't want to just redirect to the back office since this is for authing upgrades
- redirectUrl = Url.Action("AuthorizeUpgrade", "BackOffice")
- });
- }
-
- @await Html.BareMinimumServerVariablesScriptAsync(backOfficeServerVariables)
-
-
-
- @*And finally we can load in our angular app*@
-
-
-
-
-
diff --git a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Default.cshtml b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Default.cshtml
deleted file mode 100644
index f8040e91b4..0000000000
--- a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Default.cshtml
+++ /dev/null
@@ -1,35 +0,0 @@
-@using System.Globalization
-@using Umbraco.Cms.Core.Manifest
-@using Umbraco.Cms.Core.Serialization
-@using Umbraco.Cms.Web.Common.Hosting
-@using Umbraco.Extensions
-@inject IBackOfficePathGenerator BackOfficePathGenerator
-@inject IPackageManifestService PackageManifestService
-@inject IJsonSerializer JsonSerializer
-
-@{
- var backOfficePath = BackOfficePathGenerator.BackOfficePath;
- var backOfficeAssetsPath = BackOfficePathGenerator.BackOfficeAssetsPath;
-}
-
-
-
-
-
-
-
-
-
- Umbraco
-
-
-
- @await Html.BackOfficeImportMapScriptAsync(JsonSerializer, BackOfficePathGenerator, PackageManifestService)
-
-
-
-
-
-
-
-
diff --git a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoInstall/Index.cshtml b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Index.cshtml
similarity index 96%
rename from src/Umbraco.Cms.StaticAssets/umbraco/UmbracoInstall/Index.cshtml
rename to src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Index.cshtml
index f8040e91b4..0ec32181ee 100644
--- a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoInstall/Index.cshtml
+++ b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Index.cshtml
@@ -1,4 +1,5 @@
@using System.Globalization
+@using Umbraco.Cms.Api.Management.Extensions
@using Umbraco.Cms.Core.Manifest
@using Umbraco.Cms.Core.Serialization
@using Umbraco.Cms.Web.Common.Hosting
diff --git a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Preview.cshtml b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Preview.cshtml
deleted file mode 100644
index 80231d3ac5..0000000000
--- a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Preview.cshtml
+++ /dev/null
@@ -1,114 +0,0 @@
-@using Microsoft.Extensions.Options;
-@using Umbraco.Cms.Core.Configuration
-@using Umbraco.Cms.Core.Configuration.Models
-@using Umbraco.Cms.Core.Hosting
-@using Umbraco.Cms.Core.Logging
-@using Umbraco.Cms.Core.Services
-@using Umbraco.Cms.Core.WebAssets
-@using Umbraco.Cms.Infrastructure.WebAssets
-@using Umbraco.Cms.Web.BackOffice.Controllers
-@using Umbraco.Cms.Web.BackOffice.Security
-@using Umbraco.Extensions
-@inject IBackOfficeSignInManager SignInManager
-@inject BackOfficeServerVariables BackOfficeServerVariables
-@inject IUmbracoVersion UmbracoVersion
-@inject IHostingEnvironment HostingEnvironment
-@inject IOptions GlobalSettings
-@inject IRuntimeMinifier RuntimeMinifier
-@inject IProfilerHtml ProfilerHtml
-@inject ILocalizedTextService LocalizedTextService
-
-@model Umbraco.Cms.Core.Editors.BackOfficePreviewModel
-@{
- var disableDevicePreview = Model?.DisableDevicePreview.ToString().ToLowerInvariant();
-
- var EndLabel = LocalizedTextService.Localize("preview", "endLabel");
- var EndTitle = LocalizedTextService.Localize("preview", "endTitle");
- var OpenWebsiteLabel = LocalizedTextService.Localize("preview", "openWebsiteLabel");
- var OpenWebsiteTitle = LocalizedTextService.Localize("preview", "openWebsiteTitle");
- var returnToPreviewHeadline = LocalizedTextService.Localize("preview", "returnToPreviewHeadline");
- var returnToPreviewDescription = LocalizedTextService.Localize("preview", "returnToPreviewDescription");
- var returnToPreviewAcceptButton = LocalizedTextService.Localize("preview", "returnToPreviewAcceptButton");
- var returnToPreviewDeclineButton = LocalizedTextService.Localize("preview", "returnToPreviewDeclineButton");
-}
-
-
-
-
- Umbraco Preview
-
-
-
- @Html.Raw(await RuntimeMinifier.RenderCssHereAsync(BackOfficeWebAssets.UmbracoPreviewCssBundleName))
-
-
-
-
-
-
- @if (!string.IsNullOrWhiteSpace(Model?.PreviewExtendedHeaderView))
- {
- @await Html.PartialAsync(Model.PreviewExtendedHeaderView)
- }
-
-
-
-
-
-
-
-
Preview Mode
-
-
-
-
-
-
-
-
-
-
- @if (Model?.Languages.Any() == true)
- {
-
-
-
- @foreach (var language in Model.Languages)
- {
-
- }
-
-
- }
-
-
-
-
-
-
-
-
-
-
- @await Html.BareMinimumServerVariablesScriptAsync(BackOfficeServerVariables)
-
-
-
-
-
-
diff --git a/src/Umbraco.Core/Configuration/Models/ContentSettings.cs b/src/Umbraco.Core/Configuration/Models/ContentSettings.cs
index f08ab2abe5..1c2caeb9bc 100644
--- a/src/Umbraco.Core/Configuration/Models/ContentSettings.cs
+++ b/src/Umbraco.Core/Configuration/Models/ContentSettings.cs
@@ -152,9 +152,9 @@ public class ContentSettings
internal const string StaticMacroErrors = "Inline";
internal const string StaticDisallowedUploadFiles = "ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,xamlx";
internal const bool StaticShowDeprecatedPropertyEditors = false;
- internal const string StaticLoginBackgroundImage = "assets/img/login.jpg";
- internal const string StaticLoginLogoImage = "assets/img/application/umbraco_logo_blue.svg";
- internal const string StaticLoginLogoImageAlternative = "assets/img/application/umbraco_logo_blue.svg";
+ internal const string StaticLoginBackgroundImage = "login/login.jpg";
+ internal const string StaticLoginLogoImage = "login/logo_dark.svg";
+ internal const string StaticLoginLogoImageAlternative = "login/logo_light.svg";
internal const bool StaticHideBackOfficeLogo = false;
internal const bool StaticDisableDeleteWhenReferenced = false;
internal const bool StaticDisableUnpublishWhenReferenced = false;
diff --git a/src/Umbraco.Core/Constants-Security.cs b/src/Umbraco.Core/Constants-Security.cs
index b406b52731..ca8d5efb25 100644
--- a/src/Umbraco.Core/Constants-Security.cs
+++ b/src/Umbraco.Core/Constants-Security.cs
@@ -77,11 +77,6 @@ public static partial class Constants
public const string BackOfficeTokenAuthenticationType = "UmbracoBackOfficeToken";
public const string BackOfficeTwoFactorAuthenticationType = "UmbracoTwoFactorCookie";
public const string BackOfficeTwoFactorRememberMeAuthenticationType = "UmbracoTwoFactorRememberMeCookie";
- // FIXME: remove this in favor of BackOfficeAuthenticationType when the old backoffice auth is no longer necessary
- public const string NewBackOfficeAuthenticationType = "NewUmbracoBackOffice";
- public const string NewBackOfficeExternalAuthenticationType = "NewUmbracoExternalCookie";
- public const string NewBackOfficeTwoFactorAuthenticationType = "NewUmbracoTwoFactorCookie";
- public const string NewBackOfficeTwoFactorRememberMeAuthenticationType = "NewUmbracoTwoFactorRememberMeCookie";
public const string EmptyPasswordPrefix = "___UIDEMPTYPWORD__";
public const string DefaultMemberTypeAlias = "Member";
diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs
index 3b00039812..d4467fa3ea 100644
--- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs
+++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs
@@ -97,7 +97,6 @@ public static partial class UmbracoBuilderExtensions
.Append()
.Append()
.Append();
- builder.SearchableTrees().Add(() => builder.TypeLoader.GetTypes());
builder.BackOfficeAssets();
builder.SelectorHandlers().Add(() => builder.TypeLoader.GetTypes());
builder.FilterHandlers().Add(() => builder.TypeLoader.GetTypes());
@@ -245,12 +244,6 @@ public static partial class UmbracoBuilderExtensions
public static EmbedProvidersCollectionBuilder EmbedProviders(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder();
- ///
- /// Gets the back office searchable tree collection builder
- ///
- public static SearchableTreeCollectionBuilder SearchableTrees(this IUmbracoBuilder builder)
- => builder.WithCollectionBuilder();
-
///
/// Gets the back office custom assets collection builder
///
diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs
index 08586120c0..c211b54c1f 100644
--- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs
+++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs
@@ -217,7 +217,7 @@ namespace Umbraco.Cms.Core.DependencyInjection
Services.AddUnique();
Services.AddUnique();
- Services.AddUnique();
+
Services.AddUnique();
Services.AddUnique();
@@ -369,7 +369,7 @@ namespace Umbraco.Cms.Core.DependencyInjection
Services.AddUnique();
Services.AddUnique();
Services.AddUnique();
-
+
// Data type configuration cache
Services.AddUnique();
Services.AddNotificationHandler();
diff --git a/src/Umbraco.Core/Models/Mapping/ContentPropertyMapDefinition.cs b/src/Umbraco.Core/Models/Mapping/ContentPropertyMapDefinition.cs
index e5416420a4..42eebd29e2 100644
--- a/src/Umbraco.Core/Models/Mapping/ContentPropertyMapDefinition.cs
+++ b/src/Umbraco.Core/Models/Mapping/ContentPropertyMapDefinition.cs
@@ -7,6 +7,7 @@ using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Services;
+using Umbraco.Extensions;
namespace Umbraco.Cms.Core.Models.Mapping;
@@ -20,6 +21,9 @@ public class ContentPropertyMapDefinition : IMapDefinition
private readonly ContentPropertyBasicMapper _contentPropertyBasicConverter;
private readonly ContentPropertyDisplayMapper _contentPropertyDisplayMapper;
private readonly ContentPropertyDtoMapper _contentPropertyDtoConverter;
+ private readonly CommonMapper _commonMapper;
+ private readonly ContentBasicSavedStateMapper _basicStateMapper;
+
public ContentPropertyMapDefinition(
ICultureDictionary cultureDictionary,
@@ -28,8 +32,10 @@ public class ContentPropertyMapDefinition : IMapDefinition
ILocalizedTextService textService,
ILoggerFactory loggerFactory,
PropertyEditorCollection propertyEditors,
- IDataTypeConfigurationCache dataTypeConfigurationCache)
+ CommonMapper commonMapper)
{
+ _commonMapper = commonMapper;
+ _basicStateMapper = new ContentBasicSavedStateMapper();
_contentPropertyBasicConverter = new ContentPropertyBasicMapper(
dataTypeService,
entityService,
@@ -49,24 +55,6 @@ public class ContentPropertyMapDefinition : IMapDefinition
propertyEditors);
}
- [Obsolete("Please use constructor that takes an IDataTypeConfigurationCache. Will be removed in V14.")]
- public ContentPropertyMapDefinition(
- ICultureDictionary cultureDictionary,
- IDataTypeService dataTypeService,
- IEntityService entityService,
- ILocalizedTextService textService,
- ILoggerFactory loggerFactory,
- PropertyEditorCollection propertyEditors)
- : this(
- cultureDictionary,
- dataTypeService,
- entityService,
- textService,
- loggerFactory,
- propertyEditors,
- StaticServiceProvider.Instance.GetRequiredService())
- { }
-
public void DefineMaps(IUmbracoMapper mapper)
{
mapper.Define>(
@@ -74,6 +62,8 @@ public class ContentPropertyMapDefinition : IMapDefinition
mapper.Define((source, context) => new ContentPropertyBasic(), Map);
mapper.Define((source, context) => new ContentPropertyDto(), Map);
mapper.Define((source, context) => new ContentPropertyDisplay(), Map);
+ mapper.Define>((source, context) => new ContentItemBasic(), Map);
+ mapper.Define((source, context) => new ContentPropertyCollectionDto(), Map);
}
// Umbraco.Code.MapAll -Properties -Alias -Expanded
@@ -101,4 +91,81 @@ public class ContentPropertyMapDefinition : IMapDefinition
// assume this is mapping everything and no MapAll is required
_contentPropertyDisplayMapper.Map(source, target, context);
+
+ // Umbraco.Code.MapAll -Alias
+ private void Map(IContent source, ContentItemBasic target, MapperContext context)
+ {
+ target.ContentTypeId = source.ContentType.Id;
+ target.ContentTypeAlias = source.ContentType.Alias;
+ target.CreateDate = source.CreateDate;
+ target.Edited = source.Edited;
+ target.Icon = source.ContentType.Icon;
+ target.Id = source.Id;
+ target.Key = source.Key;
+ target.Name = GetName(source, context);
+ target.Owner = _commonMapper.GetOwner(source, context);
+ target.ParentId = source.ParentId;
+ target.Path = source.Path;
+ target.Properties = context.MapEnumerable(source.Properties).WhereNotNull();
+ target.SortOrder = source.SortOrder;
+ target.State = _basicStateMapper.Map(source, context);
+ target.Trashed = source.Trashed;
+ target.Udi =
+ Udi.Create(source.Blueprint ? Constants.UdiEntityType.DocumentBlueprint : Constants.UdiEntityType.Document, source.Key);
+ target.UpdateDate = GetUpdateDate(source, context);
+ target.Updater = _commonMapper.GetCreator(source, context);
+ target.VariesByCulture = source.ContentType.VariesByCulture();
+ }
+
+ // Umbraco.Code.MapAll
+ private static void Map(IContent source, ContentPropertyCollectionDto target, MapperContext context) =>
+ target.Properties = context.MapEnumerable(source.Properties).WhereNotNull();
+
+ private string? GetName(IContent source, MapperContext context)
+ {
+ // invariant = only 1 name
+ if (!source.ContentType.VariesByCulture())
+ {
+ return source.Name;
+ }
+
+ // variant = depends on culture
+ var culture = context.GetCulture();
+
+ // if there's no culture here, the issue is somewhere else (UI, whatever) - throw!
+ if (culture == null)
+ {
+ throw new InvalidOperationException("Missing culture in mapping options.");
+ }
+
+ // if we don't have a name for a culture, it means the culture is not available, and
+ // hey we should probably not be mapping it, but it's too late, return a fallback name
+ return source.CultureInfos is not null &&
+ source.CultureInfos.TryGetValue(culture, out ContentCultureInfos name) && !name.Name.IsNullOrWhiteSpace()
+ ? name.Name
+ : $"({source.Name})";
+ }
+
+ private DateTime GetUpdateDate(IContent source, MapperContext context)
+ {
+ // invariant = global date
+ if (!source.ContentType.VariesByCulture())
+ {
+ return source.UpdateDate;
+ }
+
+ // variant = depends on culture
+ var culture = context.GetCulture();
+
+ // if there's no culture here, the issue is somewhere else (UI, whatever) - throw!
+ if (culture == null)
+ {
+ throw new InvalidOperationException("Missing culture in mapping options.");
+ }
+
+ // if we don't have a date for a culture, it means the culture is not available, and
+ // hey we should probably not be mapping it, but it's too late, return a fallback date
+ DateTime? date = source.GetUpdateDate(culture);
+ return date ?? source.UpdateDate;
+ }
}
diff --git a/src/Umbraco.Core/Models/Mapping/MediaMapDefinition.cs b/src/Umbraco.Core/Models/Mapping/MediaMapDefinition.cs
new file mode 100644
index 0000000000..7a37562a82
--- /dev/null
+++ b/src/Umbraco.Core/Models/Mapping/MediaMapDefinition.cs
@@ -0,0 +1,50 @@
+using Umbraco.Cms.Core.Mapping;
+using Umbraco.Cms.Core.Models.ContentEditing;
+using Umbraco.Extensions;
+
+namespace Umbraco.Cms.Core.Models.Mapping;
+
+///
+/// Declares model mappings for media.
+///
+public class MediaMapDefinition : IMapDefinition
+{
+ private readonly CommonMapper _commonMapper;
+
+ public MediaMapDefinition(CommonMapper commonMapper)
+ {
+ _commonMapper = commonMapper;
+ }
+
+ public void DefineMaps(IUmbracoMapper mapper)
+ {
+ mapper.Define((source, context) => new ContentPropertyCollectionDto(), Map);
+ mapper.Define>((source, context) => new ContentItemBasic(), Map);
+ }
+
+ // Umbraco.Code.MapAll
+ private static void Map(IMedia source, ContentPropertyCollectionDto target, MapperContext context) =>
+ target.Properties = context.MapEnumerable(source.Properties).WhereNotNull();
+
+ // Umbraco.Code.MapAll -Edited -Updater -Alias
+ private void Map(IMedia source, ContentItemBasic target, MapperContext context)
+ {
+ target.ContentTypeId = source.ContentType.Id;
+ target.ContentTypeAlias = source.ContentType.Alias;
+ target.CreateDate = source.CreateDate;
+ target.Icon = source.ContentType.Icon;
+ target.Id = source.Id;
+ target.Key = source.Key;
+ target.Name = source.Name;
+ target.Owner = _commonMapper.GetOwner(source, context);
+ target.ParentId = source.ParentId;
+ target.Path = source.Path;
+ target.Properties = context.MapEnumerable(source.Properties).WhereNotNull();
+ target.SortOrder = source.SortOrder;
+ target.State = null;
+ target.Trashed = source.Trashed;
+ target.Udi = Udi.Create(Constants.UdiEntityType.Media, source.Key);
+ target.UpdateDate = source.UpdateDate;
+ target.VariesByCulture = source.ContentType.VariesByCulture();
+ }
+}
diff --git a/src/Umbraco.Core/Models/Mapping/MemberMapDefinition.cs b/src/Umbraco.Core/Models/Mapping/MemberMapDefinition.cs
index 8444d5bd0a..cff81c7fa9 100644
--- a/src/Umbraco.Core/Models/Mapping/MemberMapDefinition.cs
+++ b/src/Umbraco.Core/Models/Mapping/MemberMapDefinition.cs
@@ -1,13 +1,23 @@
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models.ContentEditing;
+using Umbraco.Extensions;
namespace Umbraco.Cms.Core.Models.Mapping;
///
public class MemberMapDefinition : IMapDefinition
{
+ private readonly CommonMapper _commonMapper;
+
+ public MemberMapDefinition(CommonMapper commonMapper) => _commonMapper = commonMapper;
+
///
- public void DefineMaps(IUmbracoMapper mapper) => mapper.Define(Map);
+ public void DefineMaps(IUmbracoMapper mapper)
+ {
+ mapper.Define(Map);
+ mapper.Define((source, context) => new MemberBasic(), Map);
+ mapper.Define((source, context) => new ContentPropertyCollectionDto(), Map);
+ }
private static void Map(MemberSave source, IMember target, MapperContext context)
{
@@ -28,4 +38,36 @@ public class MemberMapDefinition : IMapDefinition
// TODO: add groups as required
}
+
+ // Umbraco.Code.MapAll -Trashed -Edited -Updater -Alias -VariesByCulture
+ private void Map(IMember source, MemberBasic target, MapperContext context)
+ {
+ target.ContentTypeId = source.ContentType.Id;
+ target.ContentTypeAlias = source.ContentType.Alias;
+ target.CreateDate = source.CreateDate;
+ target.Email = source.Email;
+ target.Icon = source.ContentType.Icon;
+ target.Id = int.MaxValue;
+ target.Key = source.Key;
+ target.Name = source.Name;
+ target.Owner = _commonMapper.GetOwner(source, context);
+ target.ParentId = source.ParentId;
+ target.Path = source.Path;
+ target.Properties = context.MapEnumerable(source.Properties).WhereNotNull();
+ target.SortOrder = source.SortOrder;
+ target.State = null;
+ target.Udi = Udi.Create(Constants.UdiEntityType.Member, source.Key);
+ target.UpdateDate = source.UpdateDate;
+ target.Username = source.Username;
+ target.FailedPasswordAttempts = source.FailedPasswordAttempts;
+ target.Approved = source.IsApproved;
+ target.LockedOut = source.IsLockedOut;
+ target.LastLockoutDate = source.LastLockoutDate;
+ target.LastLoginDate = source.LastLoginDate;
+ target.LastPasswordChangeDate = source.LastPasswordChangeDate;
+ }
+
+ // Umbraco.Code.MapAll
+ private static void Map(IMember source, ContentPropertyCollectionDto target, MapperContext context) =>
+ target.Properties = context.MapEnumerable(source.Properties).WhereNotNull();
}
diff --git a/src/Umbraco.Core/Security/BackOfficeAuthenticationTypeSettings.cs b/src/Umbraco.Core/Security/BackOfficeAuthenticationTypeSettings.cs
index c0b1112ef3..fc102ce95e 100644
--- a/src/Umbraco.Core/Security/BackOfficeAuthenticationTypeSettings.cs
+++ b/src/Umbraco.Core/Security/BackOfficeAuthenticationTypeSettings.cs
@@ -4,8 +4,8 @@ namespace Umbraco.Cms.Core.Security;
public class BackOfficeAuthenticationTypeSettings
{
- public string AuthenticationType { get; set; } = Constants.Security.NewBackOfficeAuthenticationType;
- public string ExternalAuthenticationType { get; set; } = Constants.Security.NewBackOfficeExternalAuthenticationType;
- public string TwoFactorAuthenticationType { get; set; } = Constants.Security.NewBackOfficeTwoFactorAuthenticationType;
- public string TwoFactorRememberMeAuthenticationType { get; set; } = Constants.Security.NewBackOfficeTwoFactorRememberMeAuthenticationType;
+ public string AuthenticationType { get; set; } = Constants.Security.BackOfficeAuthenticationType;
+ public string ExternalAuthenticationType { get; set; } = Constants.Security.BackOfficeExternalAuthenticationType;
+ public string TwoFactorAuthenticationType { get; set; } = Constants.Security.BackOfficeTwoFactorAuthenticationType;
+ public string TwoFactorRememberMeAuthenticationType { get; set; } = Constants.Security.BackOfficeTwoFactorRememberMeAuthenticationType;
}
diff --git a/src/Umbraco.Core/Services/IIconService.cs b/src/Umbraco.Core/Services/IIconService.cs
deleted file mode 100644
index 8aff7e8920..0000000000
--- a/src/Umbraco.Core/Services/IIconService.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Umbraco.Cms.Core.Models;
-
-namespace Umbraco.Cms.Core.Services;
-
-public interface IIconService
-{
- ///
- /// Gets the svg string for the icon name found at the global icons path
- ///
- ///
- ///
- IconModel? GetIcon(string iconName);
-
- ///
- /// Gets a list of all svg icons found at at the global icons path.
- ///
- ///
- IReadOnlyDictionary? GetIcons();
-}
diff --git a/src/Umbraco.Core/Services/ITreeService.cs b/src/Umbraco.Core/Services/ITreeService.cs
deleted file mode 100644
index d61fca066a..0000000000
--- a/src/Umbraco.Core/Services/ITreeService.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using Umbraco.Cms.Core.Trees;
-
-namespace Umbraco.Cms.Core.Services;
-
-///
-/// Represents a service which manages section trees.
-///
-public interface ITreeService
-{
- ///
- /// Gets a tree.
- ///
- /// The tree alias.
- Tree? GetByAlias(string treeAlias);
-
- ///
- /// Gets all trees.
- ///
- IEnumerable GetAll(TreeUse use = TreeUse.Main);
-
- ///
- /// Gets all trees for a section.
- ///
- IEnumerable GetBySection(string sectionAlias, TreeUse use = TreeUse.Main);
-
- ///
- /// Gets all trees for a section, grouped.
- ///
- IDictionary> GetBySectionGrouped(string sectionAlias, TreeUse use = TreeUse.Main);
-}
diff --git a/src/Umbraco.Core/Services/TreeService.cs b/src/Umbraco.Core/Services/TreeService.cs
deleted file mode 100644
index 3b2b5f3618..0000000000
--- a/src/Umbraco.Core/Services/TreeService.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Umbraco.Cms.Core.Trees;
-using Umbraco.Extensions;
-
-namespace Umbraco.Cms.Core.Services;
-
-///
-/// Implements .
-///
-public class TreeService : ITreeService
-{
- private readonly TreeCollection _treeCollection;
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- public TreeService(TreeCollection treeCollection) => _treeCollection = treeCollection;
-
- ///
- public Tree? GetByAlias(string treeAlias) => _treeCollection.FirstOrDefault(x => x.TreeAlias == treeAlias);
-
- ///
- public IEnumerable GetAll(TreeUse use = TreeUse.Main)
-
- // use HasFlagAny: if use is Main|Dialog, we want to return Main *and* Dialog trees
- => _treeCollection.Where(x => x.TreeUse.HasFlagAny(use));
-
- ///
- public IEnumerable GetBySection(string sectionAlias, TreeUse use = TreeUse.Main)
-
- // use HasFlagAny: if use is Main|Dialog, we want to return Main *and* Dialog trees
- => _treeCollection.Where(x => x.SectionAlias.InvariantEquals(sectionAlias) && x.TreeUse.HasFlagAny(use))
- .OrderBy(x => x.SortOrder).ToList();
-
- ///
- public IDictionary>
- GetBySectionGrouped(string sectionAlias, TreeUse use = TreeUse.Main) =>
- GetBySection(sectionAlias, use).GroupBy(x => x.TreeGroup).ToDictionary(
- x => x.Key ?? string.Empty,
- x => (IEnumerable)x.ToArray());
-}
diff --git a/src/Umbraco.Core/Trees/SearchableTreeAttribute.cs b/src/Umbraco.Core/Trees/SearchableTreeAttribute.cs
deleted file mode 100644
index f3a92fe82f..0000000000
--- a/src/Umbraco.Core/Trees/SearchableTreeAttribute.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-namespace Umbraco.Cms.Core.Trees;
-
-[AttributeUsage(AttributeTargets.Class)]
-public sealed class SearchableTreeAttribute : Attribute
-{
- public const int DefaultSortOrder = 1000;
-
- ///
- /// This constructor will assume that the method name equals `format(searchResult, appAlias, treeAlias)`.
- ///
- /// Name of the service.
- public SearchableTreeAttribute(string serviceName)
- : this(serviceName, string.Empty)
- {
- }
-
- ///
- /// This constructor defines both the Angular service and method name to use.
- ///
- /// Name of the service.
- /// Name of the method.
- public SearchableTreeAttribute(string serviceName, string methodName)
- : this(serviceName, methodName, DefaultSortOrder)
- {
- }
-
- ///
- /// This constructor defines both the Angular service and method name to use and explicitly defines a sort order for
- /// the results
- ///
- /// Name of the service.
- /// Name of the method.
- /// The sort order.
- ///
- /// serviceName
- /// or
- /// methodName
- ///
- /// Value can't be empty or consist only of white-space characters. - serviceName
- public SearchableTreeAttribute(string serviceName, string methodName, int sortOrder)
- {
- if (serviceName == null)
- {
- throw new ArgumentNullException(nameof(serviceName));
- }
-
- if (string.IsNullOrWhiteSpace(serviceName))
- {
- throw new ArgumentException(
- "Value can't be empty or consist only of white-space characters.",
- nameof(serviceName));
- }
-
- ServiceName = serviceName;
- MethodName = methodName ?? throw new ArgumentNullException(nameof(methodName));
- SortOrder = sortOrder;
- }
-
- public string ServiceName { get; }
-
- public string MethodName { get; }
-
- public int SortOrder { get; }
-}
diff --git a/src/Umbraco.Core/Trees/SearchableTreeCollection.cs b/src/Umbraco.Core/Trees/SearchableTreeCollection.cs
deleted file mode 100644
index fdf2c8124b..0000000000
--- a/src/Umbraco.Core/Trees/SearchableTreeCollection.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using Umbraco.Cms.Core.Composing;
-using Umbraco.Cms.Core.Services;
-using Umbraco.Extensions;
-
-namespace Umbraco.Cms.Core.Trees;
-
-public class SearchableTreeCollection : BuilderCollectionBase
-{
- private readonly Dictionary _dictionary;
-
- public SearchableTreeCollection(Func> items, ITreeService treeService)
- : base(items) =>
- _dictionary = CreateDictionary(treeService);
-
- public IReadOnlyDictionary SearchableApplicationTrees => _dictionary;
-
- public SearchableApplicationTree this[string key] => _dictionary[key];
-
- private Dictionary CreateDictionary(ITreeService treeService)
- {
- Tree[] appTrees = treeService.GetAll()
- .OrderBy(x => x.SortOrder)
- .ToArray();
- var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase);
- ISearchableTree[] searchableTrees = this.ToArray();
- foreach (Tree appTree in appTrees)
- {
- ISearchableTree? found = searchableTrees.FirstOrDefault(x => x.TreeAlias.InvariantEquals(appTree.TreeAlias));
- if (found != null)
- {
- SearchableTreeAttribute? searchableTreeAttribute =
- found.GetType().GetCustomAttribute(false);
- dictionary[found.TreeAlias] = new SearchableApplicationTree(
- appTree.SectionAlias,
- appTree.TreeAlias,
- searchableTreeAttribute?.SortOrder ?? SearchableTreeAttribute.DefaultSortOrder,
- searchableTreeAttribute?.ServiceName ?? string.Empty,
- searchableTreeAttribute?.MethodName ?? string.Empty,
- found);
- }
- }
-
- return dictionary;
- }
-}
diff --git a/src/Umbraco.Core/Trees/SearchableTreeCollectionBuilder.cs b/src/Umbraco.Core/Trees/SearchableTreeCollectionBuilder.cs
deleted file mode 100644
index 372866ba68..0000000000
--- a/src/Umbraco.Core/Trees/SearchableTreeCollectionBuilder.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Umbraco.Cms.Core.Composing;
-
-namespace Umbraco.Cms.Core.Trees;
-
-public class SearchableTreeCollectionBuilder : LazyCollectionBuilderBase
-{
- protected override SearchableTreeCollectionBuilder This => this;
-
- // per request because generally an instance of ISearchableTree is a controller
- protected override ServiceLifetime CollectionLifetime => ServiceLifetime.Scoped;
-}
diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
index 47022734fb..0c8dde608d 100644
--- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
+++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
@@ -255,21 +255,6 @@ public static partial class UmbracoBuilderExtensions
return builder;
}
- public static IUmbracoBuilder AddLogViewer(this IUmbracoBuilder builder)
- {
- builder.Services.AddSingleton();
- builder.Services.AddSingleton();
- builder.SetLogViewer();
- builder.Services.AddSingleton(factory => new SerilogJsonLogViewer(
- factory.GetRequiredService>(),
- factory.GetRequiredService(),
- factory.GetRequiredService(),
- factory.GetRequiredService(),
- Log.Logger));
-
- return builder;
- }
-
///
/// Adds logging requirements for Umbraco
///
diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.MappingProfiles.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.MappingProfiles.cs
index 3fb4bfbb31..204b929166 100644
--- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.MappingProfiles.cs
+++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.MappingProfiles.cs
@@ -31,6 +31,7 @@ public static partial class UmbracoBuilderExtensions
.Add()
.Add()
.Add()
+ .Add()
.Add()
.Add()
.Add();
diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs
index f05ed4c9bd..7949eb65db 100644
--- a/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs
+++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs
@@ -150,7 +150,7 @@ public class RichTextPropertyEditor : DataEditor
.WhereNotNull()
.Select(udi => new UmbracoEntityReference(udi)));
- // references from blocks
+ // references from blocksIg
if (richTextEditorValue.Blocks is not null)
{
BlockEditorData? blockEditorData = ConvertAndClean(richTextEditorValue.Blocks);
diff --git a/src/Umbraco.Infrastructure/WebAssets/Resources.Designer.cs b/src/Umbraco.Infrastructure/WebAssets/Resources.Designer.cs
index cf2b36fdde..be54e3f199 100644
--- a/src/Umbraco.Infrastructure/WebAssets/Resources.Designer.cs
+++ b/src/Umbraco.Infrastructure/WebAssets/Resources.Designer.cs
@@ -1,7 +1,6 @@
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -62,7 +61,7 @@ namespace Umbraco.Cms.Infrastructure.WebAssets {
///
/// Looks up a localized string similar to [
- ///
+ ///
/// 'lib/jquery/jquery.min.js',
/// 'lib/jquery-ui/jquery-ui.min.js',
/// 'lib/jquery-ui-touch-punch/jquery.ui.touch-punch.min.js',
@@ -78,7 +77,7 @@ namespace Umbraco.Cms.Infrastructure.WebAssets {
/// 'lib/angular-route/angular-route.min.js',
/// 'lib/angular-cookies/angular-cookies.min.js',
/// 'lib/angular-aria/angular-aria.min.js',
- /// 'lib/angular-touch/angular-touch [rest of string was truncated]";.
+ /// 'lib/angular-touch/angular-touch.min [rest of string was truncated]";.
///
internal static string JsInitialize {
get {
@@ -130,35 +129,20 @@ namespace Umbraco.Cms.Infrastructure.WebAssets {
}
}
- ///
- /// Looks up a localized string similar to // TODO: This would be nicer as an angular module so it can be injected into stuff... that'd be heaps nicer, but
- ///// how to do that when this is not a regular JS file, it is a server side JS file and RequireJS seems to only want
- ///// to force load JS files ?
- ///
- /////create the namespace (NOTE: This loads before any dependencies so we don't have a namespace mgr so we just create it manually)
- ///var Umbraco = {};
- ///Umbraco.Sys = {};
- /////define a global static object
- ///Umbraco.Sys.ServerVariables = ##Variables## ;.
- ///
- internal static string ServerVariables {
- get {
- return ResourceManager.GetString("ServerVariables", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to [
- /// 'lib/tinymce/tinymce.min.js',
+ /// 'lib/tinymce/tinymce.min.js',
///
- /// 'lib/tinymce/plugins/anchor/plugin.min.js',
- /// 'lib/tinymce/plugins/charmap/plugin.min.js',
- /// 'lib/tinymce/plugins/table/plugin.min.js',
- /// 'lib/tinymce/plugins/lists/plugin.min.js',
- /// 'lib/tinymce/plugins/advlist/plugin.min.js',
- /// 'lib/tinymce/plugins/autolink/plugin.min.js',
- /// 'lib/tinymce/plugins/directionality/plugin.min.js',
- /// 'lib/tinymce/plugins/t [rest of string was truncated]";.
+ /// 'lib/tinymce/plugins/anchor/plugin.min.js',
+ /// 'lib/tinymce/plugins/charmap/plugin.min.js',
+ /// 'lib/tinymce/plugins/table/plugin.min.js',
+ /// 'lib/tinymce/plugins/lists/plugin.min.js',
+ /// 'lib/tinymce/plugins/advlist/plugin.min.js',
+ /// 'lib/tinymce/plugins/autolink/plugin.min.js',
+ /// 'lib/tinymce/plugins/directionality/plugin.min.js',
+ /// 'lib/tinymce/plugins/searchreplace/plugin.min.js'
+ ///]
+ ///.
///
internal static string TinyMceInitialize {
get {
diff --git a/src/Umbraco.Infrastructure/WebAssets/Resources.resx b/src/Umbraco.Infrastructure/WebAssets/Resources.resx
index 476acc461b..0c0289a6f6 100644
--- a/src/Umbraco.Infrastructure/WebAssets/Resources.resx
+++ b/src/Umbraco.Infrastructure/WebAssets/Resources.resx
@@ -28,9 +28,6 @@
PreviewInitialize.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
-
- ServerVariables.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
-
TinyMceInitialize.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
diff --git a/src/Umbraco.Infrastructure/WebAssets/ServerVariables.js b/src/Umbraco.Infrastructure/WebAssets/ServerVariables.js
deleted file mode 100644
index 4a0017550a..0000000000
--- a/src/Umbraco.Infrastructure/WebAssets/ServerVariables.js
+++ /dev/null
@@ -1,9 +0,0 @@
-// TODO: This would be nicer as an angular module so it can be injected into stuff... that'd be heaps nicer, but
-// how to do that when this is not a regular JS file, it is a server side JS file and RequireJS seems to only want
-// to force load JS files ?
-
-//create the namespace (NOTE: This loads before any dependencies so we don't have a namespace mgr so we just create it manually)
-var Umbraco = {};
-Umbraco.Sys = {};
-//define a global static object
-Umbraco.Sys.ServerVariables = ##Variables## ;
\ No newline at end of file
diff --git a/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParser.cs b/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParser.cs
deleted file mode 100644
index bcdc316fe5..0000000000
--- a/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParser.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Newtonsoft.Json.Linq;
-using Umbraco.Cms.Core.Events;
-using Umbraco.Cms.Core.Notifications;
-
-namespace Umbraco.Cms.Infrastructure.WebAssets;
-
-///
-/// Ensures the server variables are included in the outgoing JS script
-///
-public class ServerVariablesParser
-{
- private const string Token = "##Variables##";
- private readonly IEventAggregator _eventAggregator;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public ServerVariablesParser(IEventAggregator eventAggregator) => _eventAggregator = eventAggregator;
-
- ///
- /// Ensures the server variables in the dictionary are included in the outgoing JS script
- ///
- public async Task ParseAsync(Dictionary items)
- {
- var vars = Resources.ServerVariables;
-
- // Raise event for developers to add custom variables
- await _eventAggregator.PublishAsync(new ServerVariablesParsingNotification(items));
-
- var json = JObject.FromObject(items);
- return vars.Replace(Token, json.ToString());
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/ActionResults/JavaScriptResult.cs b/src/Umbraco.Web.BackOffice/ActionResults/JavaScriptResult.cs
deleted file mode 100644
index 5344a22707..0000000000
--- a/src/Umbraco.Web.BackOffice/ActionResults/JavaScriptResult.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-
-namespace Umbraco.Cms.Web.BackOffice.ActionResults;
-
-public class JavaScriptResult : ContentResult
-{
- public JavaScriptResult(string? script)
- {
- Content = script;
- ContentType = "application/javascript";
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/ActionResults/UmbracoErrorResult.cs b/src/Umbraco.Web.BackOffice/ActionResults/UmbracoErrorResult.cs
deleted file mode 100644
index a2ae808b19..0000000000
--- a/src/Umbraco.Web.BackOffice/ActionResults/UmbracoErrorResult.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System.Net;
-using Microsoft.AspNetCore.Mvc;
-
-namespace Umbraco.Cms.Web.BackOffice.ActionResults;
-
-public class UmbracoErrorResult : ObjectResult
-{
- public UmbracoErrorResult(HttpStatusCode statusCode, string message) : this(statusCode, new MessageWrapper(message))
- {
- }
-
- public UmbracoErrorResult(HttpStatusCode statusCode, object value) : base(value) => StatusCode = (int)statusCode;
-
- private class MessageWrapper
- {
- public MessageWrapper(string message) => Message = message;
-
- public string Message { get; }
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/AdminUsersHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/AdminUsersHandler.cs
deleted file mode 100644
index b5cc970025..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/AdminUsersHandler.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using System.Globalization;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Primitives;
-using Umbraco.Cms.Core;
-using Umbraco.Cms.Core.Editors;
-using Umbraco.Cms.Core.Models.Membership;
-using Umbraco.Cms.Core.Security;
-using Umbraco.Cms.Core.Services;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// If the users being edited is an admin then we must ensure that the current user is also an admin.
-///
-public class AdminUsersHandler : MustSatisfyRequirementAuthorizationHandler
-{
- private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
- private readonly IHttpContextAccessor _httpContextAccessor;
- private readonly UserEditorAuthorizationHelper _userEditorAuthorizationHelper;
- private readonly IUserService _userService;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Accessor for the HTTP context of the current request.
- /// Service for user related operations.
- /// Accessor for back-office security.
- /// Helper for user authorization checks.
- public AdminUsersHandler(
- IHttpContextAccessor httpContextAccessor,
- IUserService userService,
- IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
- UserEditorAuthorizationHelper userEditorAuthorizationHelper)
- {
- _httpContextAccessor = httpContextAccessor;
- _userService = userService;
- _backOfficeSecurityAccessor = backOfficeSecurityAccessor;
- _userEditorAuthorizationHelper = userEditorAuthorizationHelper;
- }
-
- ///
- protected override Task IsAuthorized(AuthorizationHandlerContext context, AdminUsersRequirement requirement)
- {
- StringValues? queryString = _httpContextAccessor.HttpContext?.Request.Query[requirement.QueryStringName];
- if (!queryString.HasValue || !queryString.Value.Any())
- {
- // Must succeed this requirement since we cannot process it.
- return Task.FromResult(true);
- }
-
- int[]? userIds;
- if (int.TryParse(queryString, NumberStyles.Integer, CultureInfo.InvariantCulture, out var userId))
- {
- userIds = new[] { userId };
- }
- else
- {
- var ids = queryString.ToString()?.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries)
- .ToList();
- if (ids?.Count == 0)
- {
- // Must succeed this requirement since we cannot process it.
- return Task.FromResult(true);
- }
-
- userIds = ids?
- .Select(x =>
- int.TryParse(x, NumberStyles.Integer, CultureInfo.InvariantCulture, out var output)
- ? Attempt.Succeed(output)
- : Attempt.Fail())
- .Where(x => x.Success)
- .Select(x => x.Result)
- .ToArray();
- }
-
- if (userIds?.Length == 0)
- {
- // Must succeed this requirement since we cannot process it.
- return Task.FromResult(true);
- }
-
- IEnumerable users = _userService.GetUsersById(userIds);
- var isAuth = users.All(user =>
- _userEditorAuthorizationHelper.IsAuthorized(_backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser,
- user, null, null, null) != false);
-
- return Task.FromResult(isAuth);
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/AdminUsersRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/AdminUsersRequirement.cs
deleted file mode 100644
index 2c2aea0ff2..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/AdminUsersRequirement.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Authorization requirement for the
-///
-public class AdminUsersRequirement : IAuthorizationRequirement
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// Query string name from which to authorize values.
- public AdminUsersRequirement(string queryStringName = "id") => QueryStringName = queryStringName;
-
- ///
- /// Gets the query string name from which to authorize values.
- ///
- public string QueryStringName { get; }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/BackOfficeHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/BackOfficeHandler.cs
deleted file mode 100644
index 451aec2d89..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/BackOfficeHandler.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-using Umbraco.Cms.Core.Security;
-using Umbraco.Cms.Core.Services;
-using Umbraco.Extensions;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Ensures authorization is successful for a back office user.
-///
-public class BackOfficeHandler : MustSatisfyRequirementAuthorizationHandler
-{
- private readonly IBackOfficeSecurityAccessor _backOfficeSecurity;
- private readonly IRuntimeState _runtimeState;
-
- public BackOfficeHandler(IBackOfficeSecurityAccessor backOfficeSecurity, IRuntimeState runtimeState)
- {
- _backOfficeSecurity = backOfficeSecurity;
- _runtimeState = runtimeState;
- }
-
- protected override Task IsAuthorized(AuthorizationHandlerContext context, BackOfficeRequirement requirement)
- {
- // if not configured (install or upgrade) then we can continue
- // otherwise we need to ensure that a user is logged in
-
- switch (_runtimeState.Level)
- {
- case var _ when _runtimeState.EnableInstaller():
- return Task.FromResult(true);
- default:
- if (!_backOfficeSecurity.BackOfficeSecurity?.IsAuthenticated() ?? false)
- {
- return Task.FromResult(false);
- }
-
- var userApprovalSucceeded = !requirement.RequireApproval ||
- (_backOfficeSecurity.BackOfficeSecurity?.CurrentUser?.IsApproved ?? false);
- return Task.FromResult(userApprovalSucceeded);
- }
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/BackOfficeRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/BackOfficeRequirement.cs
deleted file mode 100644
index d0d5ae1913..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/BackOfficeRequirement.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Authorization requirement for the .
-///
-public class BackOfficeRequirement : IAuthorizationRequirement
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// Flag for whether back-office user approval is required.
- public BackOfficeRequirement(bool requireApproval = true) => RequireApproval = requireApproval;
-
- ///
- /// Gets a value indicating whether back-office user approval is required.
- ///
- public bool RequireApproval { get; }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsPublishBranchHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsPublishBranchHandler.cs
deleted file mode 100644
index faf67d8ec5..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsPublishBranchHandler.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-using Umbraco.Cms.Core.Models;
-using Umbraco.Cms.Core.Models.Entities;
-using Umbraco.Cms.Core.Models.Membership;
-using Umbraco.Cms.Core.Security;
-using Umbraco.Cms.Core.Services;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// The user must have access to all descendant nodes of the content item in order to continue.
-///
-public class ContentPermissionsPublishBranchHandler : MustSatisfyRequirementAuthorizationHandler<
- ContentPermissionsPublishBranchRequirement, IContent>
-{
- private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
- private readonly ContentPermissions _contentPermissions;
- private readonly IEntityService _entityService;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Service for entity operations.
- /// per for user content authorization checks.
- /// Accessor for back-office security.
- public ContentPermissionsPublishBranchHandler(
- IEntityService entityService,
- ContentPermissions contentPermissions,
- IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
- {
- _entityService = entityService;
- _contentPermissions = contentPermissions;
- _backOfficeSecurityAccessor = backOfficeSecurityAccessor;
- }
-
- ///
- protected override Task IsAuthorized(AuthorizationHandlerContext context,
- ContentPermissionsPublishBranchRequirement requirement, IContent resource)
- {
- IUser? currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser;
-
- var denied = new List();
- var page = 0;
- const int pageSize = 500;
- var total = long.MaxValue;
-
- while (page * pageSize < total)
- {
- // Order descendents by shallowest to deepest, this allows us to check permissions from top to bottom so we can exit
- // early if a permission higher up fails.
- IEnumerable descendants = _entityService.GetPagedDescendants(
- resource.Id,
- UmbracoObjectTypes.Document,
- page++,
- pageSize,
- out total,
- ordering: Ordering.By("path"));
-
- foreach (IEntitySlim c in descendants)
- {
- // If this item's path has already been denied or if the user doesn't have access to it, add to the deny list.
- if (denied.Any(x => c.Path.StartsWith($"{x.Path},")) ||
- _contentPermissions.CheckPermissions(
- c,
- currentUser,
- requirement.Permission) == ContentPermissions.ContentAccess.Denied)
- {
- denied.Add(c);
- }
- }
- }
-
- return Task.FromResult(denied.Count == 0);
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsPublishBranchRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsPublishBranchRequirement.cs
deleted file mode 100644
index 4bb1636b63..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsPublishBranchRequirement.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Authorization requirement for
-///
-public class ContentPermissionsPublishBranchRequirement : IAuthorizationRequirement
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// Permission to check.
- public ContentPermissionsPublishBranchRequirement(char permission) => Permission = permission;
-
- ///
- /// Gets a value for the permission to check.
- ///
- public char Permission { get; }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsQueryStringHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsQueryStringHandler.cs
deleted file mode 100644
index 15d0b39f65..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsQueryStringHandler.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Primitives;
-using Umbraco.Cms.Core.Models;
-using Umbraco.Cms.Core.Security;
-using Umbraco.Cms.Core.Services;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Used to authorize if the user has the correct permission access to the content for the content id specified in a
-/// query string.
-///
-public class
- ContentPermissionsQueryStringHandler : PermissionsQueryStringHandler
-{
- private readonly ContentPermissions _contentPermissions;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Accessor for back-office security.
- /// Accessor for the HTTP context of the current request.
- /// Service for entity operations.
- /// Helper for content authorization checks.
- public ContentPermissionsQueryStringHandler(
- IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
- IHttpContextAccessor httpContextAccessor,
- IEntityService entityService,
- ContentPermissions contentPermissions)
- : base(backOfficeSecurityAccessor, httpContextAccessor, entityService) =>
- _contentPermissions = contentPermissions;
-
- ///
- protected override Task IsAuthorized(AuthorizationHandlerContext context, ContentPermissionsQueryStringRequirement requirement)
- {
- int nodeId;
- if (requirement.NodeId.HasValue == false)
- {
- if (HttpContextAccessor.HttpContext is null || requirement.QueryStringName is null ||
- !HttpContextAccessor.HttpContext.Request.Query.TryGetValue(requirement.QueryStringName, out StringValues routeVal))
- {
- // Must succeed this requirement since we cannot process it
- return Task.FromResult(true);
- }
-
- var argument = routeVal.ToString();
-
- if (!TryParseNodeId(argument, out nodeId))
- {
- // Must succeed this requirement since we cannot process it.
- return Task.FromResult(true);
- }
- }
- else
- {
- nodeId = requirement.NodeId.Value;
- }
-
- ContentPermissions.ContentAccess permissionResult = _contentPermissions.CheckPermissions(
- nodeId,
- BackOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser,
- out IContent? contentItem,
- new[] { requirement.PermissionToCheck });
-
- if (HttpContextAccessor.HttpContext is not null && contentItem is not null)
- {
- // Store the content item in request cache so it can be resolved in the controller without re-looking it up.
- HttpContextAccessor.HttpContext.Items[typeof(IContent).ToString()] = contentItem;
- }
-
- return permissionResult switch
- {
- ContentPermissions.ContentAccess.Denied => Task.FromResult(false),
- _ => Task.FromResult(true)
- };
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsQueryStringRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsQueryStringRequirement.cs
deleted file mode 100644
index bdeeeef2cc..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsQueryStringRequirement.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// An authorization requirement for
-///
-public class ContentPermissionsQueryStringRequirement : IAuthorizationRequirement
-{
- ///
- /// Initializes a new instance of the class for a specific node
- /// id.
- ///
- /// The node Id.
- /// The permission to authorize the current user against.
- public ContentPermissionsQueryStringRequirement(int nodeId, char permissionToCheck)
- {
- NodeId = nodeId;
- PermissionToCheck = permissionToCheck;
- }
-
- ///
- /// Initializes a new instance of the class for a
- /// node id based on a query string parameter.
- ///
- /// The querystring parameter name.
- /// The permission to authorize the current user against.
- public ContentPermissionsQueryStringRequirement(char permissionToCheck, string paramName = "id")
- {
- QueryStringName = paramName;
- PermissionToCheck = permissionToCheck;
- }
-
- ///
- /// Gets the specific node Id.
- ///
- public int? NodeId { get; }
-
- ///
- /// Gets the querystring parameter name.
- ///
- public string? QueryStringName { get; }
-
- ///
- /// Gets the permission to authorize the current user against.
- ///
- public char PermissionToCheck { get; }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResource.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResource.cs
deleted file mode 100644
index d83318531a..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResource.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Umbraco.Cms.Core.Models;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// The resource used for the
-///
-public class ContentPermissionsResource
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The content.
- /// The permission to authorize.
- public ContentPermissionsResource(IContent? content, char permissionToCheck)
- {
- PermissionsToCheck = new List { permissionToCheck };
- Content = content;
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The content.
- /// The collection of permissions to authorize.
- public ContentPermissionsResource(IContent content, IReadOnlyList permissionsToCheck)
- {
- Content = content;
- PermissionsToCheck = permissionsToCheck;
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The content.
- /// The node Id.
- /// The collection of permissions to authorize.
- public ContentPermissionsResource(IContent? content, int nodeId, IReadOnlyList permissionsToCheck)
- {
- Content = content;
- NodeId = nodeId;
- PermissionsToCheck = permissionsToCheck;
- }
-
- ///
- /// Gets the node Id.
- ///
- public int? NodeId { get; }
-
- ///
- /// Gets the collection of permissions to authorize.
- ///
- public IReadOnlyList PermissionsToCheck { get; }
-
- ///
- /// Gets the content.
- ///
- public IContent? Content { get; }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceHandler.cs
deleted file mode 100644
index e453787c33..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceHandler.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-using Umbraco.Cms.Core.Models;
-using Umbraco.Cms.Core.Security;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Used to authorize if the user has the correct permission access to the content for the
-/// specified.
-///
-public class ContentPermissionsResourceHandler : MustSatisfyRequirementAuthorizationHandler<
- ContentPermissionsResourceRequirement, ContentPermissionsResource>
-{
- private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
- private readonly ContentPermissions _contentPermissions;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Accessor for back-office security.
- /// Helper for content authorization checks.
- public ContentPermissionsResourceHandler(
- IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
- ContentPermissions contentPermissions)
- {
- _backOfficeSecurityAccessor = backOfficeSecurityAccessor;
- _contentPermissions = contentPermissions;
- }
-
- ///
- protected override Task IsAuthorized(AuthorizationHandlerContext context,
- ContentPermissionsResourceRequirement requirement, ContentPermissionsResource resource)
- {
- ContentPermissions.ContentAccess permissionResult = resource.NodeId.HasValue
- ? _contentPermissions.CheckPermissions(
- resource.NodeId.Value,
- _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser,
- out IContent? _,
- resource.PermissionsToCheck)
- : _contentPermissions.CheckPermissions(
- resource.Content,
- _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser,
- resource.PermissionsToCheck);
-
- return Task.FromResult(permissionResult != ContentPermissions.ContentAccess.Denied);
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceRequirement.cs
deleted file mode 100644
index a25d491604..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceRequirement.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// An authorization requirement for
-///
-public class ContentPermissionsResourceRequirement : IAuthorizationRequirement
-{
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginHandler.cs
deleted file mode 100644
index 7daa9fbdd0..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginHandler.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-using Umbraco.Cms.Web.BackOffice.Security;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Ensures the resource cannot be accessed if
-/// returns true.
-///
-public class DenyLocalLoginHandler : MustSatisfyRequirementAuthorizationHandler
-{
- private readonly IBackOfficeExternalLoginProviders _externalLogins;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Provides access to instances.
- public DenyLocalLoginHandler(IBackOfficeExternalLoginProviders externalLogins) => _externalLogins = externalLogins;
-
- ///
- protected override Task IsAuthorized(AuthorizationHandlerContext context,
- DenyLocalLoginRequirement requirement) =>
- Task.FromResult(!_externalLogins.HasDenyLocalLogin());
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginRequirement.cs
deleted file mode 100644
index 3aade0f9fd..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginRequirement.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Marker requirement for the .
-///
-public class DenyLocalLoginRequirement : IAuthorizationRequirement
-{
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsQueryStringHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsQueryStringHandler.cs
deleted file mode 100644
index 7b662e5fc0..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsQueryStringHandler.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Primitives;
-using Umbraco.Cms.Core.Models;
-using Umbraco.Cms.Core.Security;
-using Umbraco.Cms.Core.Services;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Used to authorize if the user has the correct permission access to the media for the media id specified in a query
-/// string.
-///
-public class MediaPermissionsQueryStringHandler : PermissionsQueryStringHandler
-{
- private readonly MediaPermissions _mediaPermissions;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Accessor for back-office security.
- /// Accessor for the HTTP context of the current request.
- /// Service for entity operations.
- /// Helper for media authorization checks.
- public MediaPermissionsQueryStringHandler(
- IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
- IHttpContextAccessor httpContextAccessor,
- IEntityService entityService,
- MediaPermissions mediaPermissions)
- : base(backOfficeSecurityAccessor, httpContextAccessor, entityService) => _mediaPermissions = mediaPermissions;
-
- ///
- protected override Task IsAuthorized(AuthorizationHandlerContext context,
- MediaPermissionsQueryStringRequirement requirement)
- {
- if (HttpContextAccessor.HttpContext is null ||
- !HttpContextAccessor.HttpContext.Request.Query.TryGetValue(requirement.QueryStringName,
- out StringValues routeVal))
- {
- // Must succeed this requirement since we cannot process it.
- return Task.FromResult(true);
- }
-
- var argument = routeVal.ToString();
-
- if (!TryParseNodeId(argument, out var nodeId))
- {
- // Must succeed this requirement since we cannot process it.
- return Task.FromResult(true);
- }
-
- MediaPermissions.MediaAccess permissionResult = _mediaPermissions.CheckPermissions(
- BackOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser,
- nodeId,
- out IMedia? mediaItem);
-
- if (mediaItem != null)
- {
- // Store the media item in request cache so it can be resolved in the controller without re-looking it up.
- HttpContextAccessor.HttpContext.Items[typeof(IMedia).ToString()] = mediaItem;
- }
-
- return permissionResult switch
- {
- MediaPermissions.MediaAccess.Denied => Task.FromResult(false),
- _ => Task.FromResult(true)
- };
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsQueryStringRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsQueryStringRequirement.cs
deleted file mode 100644
index 5174fe54de..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsQueryStringRequirement.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// An authorization requirement for
-///
-public class MediaPermissionsQueryStringRequirement : IAuthorizationRequirement
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// Querystring paramter name.
- public MediaPermissionsQueryStringRequirement(string paramName) => QueryStringName = paramName;
-
- ///
- /// Gets the querystring paramter name.
- ///
- public string QueryStringName { get; }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResource.cs b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResource.cs
deleted file mode 100644
index 562de479ca..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResource.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Umbraco.Cms.Core.Models;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-public class MediaPermissionsResource
-{
- public MediaPermissionsResource(IMedia? media) => Media = media;
-
- public MediaPermissionsResource(int nodeId) => NodeId = nodeId;
-
- public int? NodeId { get; }
- public IMedia? Media { get; }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceHandler.cs
deleted file mode 100644
index 06e2b4f89c..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceHandler.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-using Umbraco.Cms.Core.Models;
-using Umbraco.Cms.Core.Security;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Used to authorize if the user has the correct permission access to the content for the
-/// specified.
-///
-public class MediaPermissionsResourceHandler : MustSatisfyRequirementAuthorizationHandler<
- MediaPermissionsResourceRequirement, MediaPermissionsResource>
-{
- private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
- private readonly MediaPermissions _mediaPermissions;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Accessor for back-office security.
- /// Helper for media authorization checks.
- public MediaPermissionsResourceHandler(
- IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
- MediaPermissions mediaPermissions)
- {
- _backOfficeSecurityAccessor = backOfficeSecurityAccessor;
- _mediaPermissions = mediaPermissions;
- }
-
- ///
- protected override Task IsAuthorized(AuthorizationHandlerContext context,
- MediaPermissionsResourceRequirement requirement, MediaPermissionsResource resource)
- {
- MediaPermissions.MediaAccess permissionResult = resource.NodeId.HasValue
- ? _mediaPermissions.CheckPermissions(
- _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser,
- resource.NodeId.Value,
- out _)
- : _mediaPermissions.CheckPermissions(
- resource.Media,
- _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser);
-
- return Task.FromResult(permissionResult != MediaPermissions.MediaAccess.Denied);
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceRequirement.cs
deleted file mode 100644
index 5251174761..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceRequirement.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// An authorization requirement for
-///
-public class MediaPermissionsResourceRequirement : IAuthorizationRequirement
-{
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/MustSatisfyRequirementAuthorizationHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/MustSatisfyRequirementAuthorizationHandler.cs
deleted file mode 100644
index 6d3f6f3187..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/MustSatisfyRequirementAuthorizationHandler.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Abstract handler that must satisfy the requirement so Succeed or Fail will be called no matter what.
-///
-/// Authorization requirement.
-///
-/// aspnetcore Authz handlers are not required to satisfy the requirement and generally don't explicitly call Fail when
-/// the requirement
-/// isn't satisfied, however in many simple cases explicitly calling Succeed or Fail is what we want which is what this
-/// class is used for.
-///
-public abstract class MustSatisfyRequirementAuthorizationHandler : AuthorizationHandler
- where T : IAuthorizationRequirement
-{
- ///
- protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, T requirement)
- {
- var isAuth = await IsAuthorized(context, requirement);
- if (isAuth)
- {
- context.Succeed(requirement);
- }
- else
- {
- context.Fail();
- }
- }
-
- ///
- /// Return true if the requirement is succeeded or ignored, return false if the requirement is explicitly not met
- ///
- /// The authorization context.
- /// The authorization requirement.
- /// True if request is authorized, false if not.
- protected abstract Task IsAuthorized(AuthorizationHandlerContext context, T requirement);
-}
-
-///
-/// Abstract handler that must satisfy the requirement so Succeed or Fail will be called no matter what.
-///
-/// Authorization requirement.
-/// Resource to authorize access to.
-///
-/// aspnetcore Authz handlers are not required to satisfy the requirement and generally don't explicitly call Fail when
-/// the requirement
-/// isn't satisfied, however in many simple cases explicitly calling Succeed or Fail is what we want which is what this
-/// class is used for.
-///
-public abstract class MustSatisfyRequirementAuthorizationHandler : AuthorizationHandler
- where T : IAuthorizationRequirement
-{
- ///
- protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, T requirement,
- TResource resource)
- {
- var isAuth = await IsAuthorized(context, requirement, resource);
- if (isAuth)
- {
- context.Succeed(requirement);
- }
- else
- {
- context.Fail();
- }
- }
-
- ///
- /// Return true if the requirement is succeeded or ignored, return false if the requirement is explicitly not met
- ///
- /// The authorization context.
- /// The authorization requirement.
- /// The resource to authorize access to.
- /// True if request is authorized, false if not.
- protected abstract Task IsAuthorized(AuthorizationHandlerContext context, T requirement, TResource resource);
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/PermissionsQueryStringHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/PermissionsQueryStringHandler.cs
deleted file mode 100644
index 5367208c79..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/PermissionsQueryStringHandler.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using System.Globalization;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Umbraco.Cms.Core;
-using Umbraco.Cms.Core.Models;
-using Umbraco.Cms.Core.Security;
-using Umbraco.Cms.Core.Services;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Abstract base class providing common functionality for authorization checks based on querystrings.
-///
-/// Authorization requirement
-public abstract class PermissionsQueryStringHandler : MustSatisfyRequirementAuthorizationHandler
- where T : IAuthorizationRequirement
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// Accessor for back-office security.
- /// Accessor for the HTTP context of the current request.
- /// Service for entity operations.
- public PermissionsQueryStringHandler(
- IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
- IHttpContextAccessor httpContextAccessor,
- IEntityService entityService)
- {
- BackOfficeSecurityAccessor = backOfficeSecurityAccessor;
- HttpContextAccessor = httpContextAccessor;
- EntityService = entityService;
- }
-
- ///
- /// Gets or sets the instance.
- ///
- protected IBackOfficeSecurityAccessor BackOfficeSecurityAccessor { get; set; }
-
- ///
- /// Gets or sets the instance.
- ///
- protected IHttpContextAccessor HttpContextAccessor { get; set; }
-
- ///
- /// Gets or sets the instance.
- ///
- protected IEntityService EntityService { get; set; }
-
- ///
- /// Attempts to parse a node ID from a string representation found in a querystring value.
- ///
- /// Querystring value.
- /// Output parsed Id.
- /// True of node ID could be parased, false it not.
- protected bool TryParseNodeId(string argument, out int nodeId)
- {
- // If the argument is an int, it will parse and can be assigned to nodeId.
- // It might be a udi, so check that next.
- // Otherwise treat it as a guid - unlikely we ever get here.
- // Failing that, we can't parse it.
- if (int.TryParse(argument, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedId))
- {
- nodeId = parsedId;
- return true;
- }
-
- if (UdiParser.TryParse(argument, true, out Udi? udi))
- {
- nodeId = EntityService.GetId(udi).Result;
- return true;
- }
-
- if (Guid.TryParse(argument, out Guid key))
- {
- nodeId = EntityService.GetId(key, UmbracoObjectTypes.Document).Result;
- return true;
- }
-
- nodeId = 0;
- return false;
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/SectionHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/SectionHandler.cs
deleted file mode 100644
index 2bcaa9a89d..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/SectionHandler.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-using Umbraco.Cms.Core.Security;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Ensures that the current user has access to the section
-///
-///
-/// The user only needs access to one of the sections specified, not all of the sections.
-///
-public class SectionHandler : MustSatisfyRequirementAuthorizationHandler
-{
- private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Accessor for back-office security.
- public SectionHandler(IBackOfficeSecurityAccessor backOfficeSecurityAccessor) =>
- _backOfficeSecurityAccessor = backOfficeSecurityAccessor;
-
- ///
- protected override Task IsAuthorized(AuthorizationHandlerContext context, SectionRequirement requirement)
- {
- var authorized = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser != null &&
- requirement.SectionAliases
- .Any(app => _backOfficeSecurityAccessor.BackOfficeSecurity.UserHasSectionAccess(
- app, _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser));
-
- return Task.FromResult(authorized);
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/SectionRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/SectionRequirement.cs
deleted file mode 100644
index ab0d3c47f9..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/SectionRequirement.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Authorization requirements for
-///
-public class SectionRequirement : IAuthorizationRequirement
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// Aliases for sections that the user will need access to.
- public SectionRequirement(params string[] aliases) => SectionAliases = aliases;
-
- ///
- /// Gets the aliases for sections that the user will need access to.
- ///
- public IReadOnlyCollection SectionAliases { get; }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/TreeHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/TreeHandler.cs
deleted file mode 100644
index 07f2b96eed..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/TreeHandler.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-using Umbraco.Cms.Core.Security;
-using Umbraco.Cms.Core.Services;
-using Umbraco.Extensions;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Ensures that the current user has access to the section for which the specified tree(s) belongs
-///
-///
-/// This would allow a tree to be moved between sections.
-/// The user only needs access to one of the trees specified, not all of the trees.
-///
-public class TreeHandler : MustSatisfyRequirementAuthorizationHandler
-{
- private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
- private readonly ITreeService _treeService;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Service for section tree operations.
- /// Accessor for back-office security.
- public TreeHandler(ITreeService treeService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
- {
- _treeService = treeService ?? throw new ArgumentNullException(nameof(treeService));
- _backOfficeSecurityAccessor = backOfficeSecurityAccessor ??
- throw new ArgumentNullException(nameof(backOfficeSecurityAccessor));
- }
-
- ///
- protected override Task IsAuthorized(AuthorizationHandlerContext context, TreeRequirement requirement)
- {
- var apps = requirement.TreeAliases
- .Select(x => _treeService.GetByAlias(x))
- .WhereNotNull()
- .Select(x => x.SectionAlias)
- .Distinct()
- .ToArray();
-
- var isAuth = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser != null &&
- apps.Any(app => _backOfficeSecurityAccessor.BackOfficeSecurity.UserHasSectionAccess(
- app, _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser));
-
- return Task.FromResult(isAuth);
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/TreeRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/TreeRequirement.cs
deleted file mode 100644
index b5bf5bf815..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/TreeRequirement.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Authorization requirements for
-///
-public class TreeRequirement : IAuthorizationRequirement
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The aliases for trees that the user will need access to.
- public TreeRequirement(params string[] aliases) => TreeAliases = aliases;
-
- ///
- /// Gets the aliases for trees that the user will need access to.
- ///
- public IReadOnlyCollection TreeAliases { get; }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/UserGroupHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/UserGroupHandler.cs
deleted file mode 100644
index 82a01ab73e..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/UserGroupHandler.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using System.Globalization;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Primitives;
-using Umbraco.Cms.Core;
-using Umbraco.Cms.Core.Cache;
-using Umbraco.Cms.Core.Models.Membership;
-using Umbraco.Cms.Core.Security;
-using Umbraco.Cms.Core.Services;
-using Umbraco.Cms.Web.BackOffice.Controllers;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Authorizes that the current user has access to the user group Id in the request
-///
-public class UserGroupHandler : MustSatisfyRequirementAuthorizationHandler
-{
- private readonly AppCaches _appCaches;
- private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
- private readonly IContentService _contentService;
- private readonly IEntityService _entityService;
- private readonly IHttpContextAccessor _httpContextAccessor;
- private readonly IMediaService _mediaService;
- private readonly IUserService _userService;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Accessor for the HTTP context of the current request.
- /// Service for user related operations.
- /// Service for content related operations.
- /// Service for media related operations.
- /// Service for entity related operations.
- /// Accessor for back-office security.
- /// App caches.
- public UserGroupHandler(
- IHttpContextAccessor httpContextAccessor,
- IUserService userService,
- IContentService contentService,
- IMediaService mediaService,
- IEntityService entityService,
- IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
- AppCaches appCaches)
- {
- _httpContextAccessor = httpContextAccessor;
- _userService = userService;
- _contentService = contentService;
- _mediaService = mediaService;
- _entityService = entityService;
- _backOfficeSecurityAccessor = backOfficeSecurityAccessor;
- _appCaches = appCaches;
- }
-
- ///
- protected override Task IsAuthorized(AuthorizationHandlerContext context, UserGroupRequirement requirement)
- {
- IUser? currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser;
-
- StringValues? querystring = _httpContextAccessor.HttpContext?.Request.Query[requirement.QueryStringName];
- if (querystring is null)
- {
- // Must succeed this requirement since we cannot process it.
- return Task.FromResult(true);
- }
-
- if (querystring.Value.Count == 0)
- {
- // Must succeed this requirement since we cannot process it.
- return Task.FromResult(true);
- }
-
- var intIds = querystring.Value.ToString().Split(Constants.CharArrays.Comma)
- .Select(x =>
- int.TryParse(x, NumberStyles.Integer, CultureInfo.InvariantCulture, out var output)
- ? Attempt.Succeed(output)
- : Attempt.Fail())
- .Where(x => x.Success).Select(x => x.Result).ToArray();
-
- var authHelper = new UserGroupEditorAuthorizationHelper(
- _userService,
- _contentService,
- _mediaService,
- _entityService,
- _appCaches);
-
- Attempt isAuth = authHelper.AuthorizeGroupAccess(currentUser, intIds);
-
- return Task.FromResult(isAuth.Success);
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/UserGroupRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/UserGroupRequirement.cs
deleted file mode 100644
index c06638f273..0000000000
--- a/src/Umbraco.Web.BackOffice/Authorization/UserGroupRequirement.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Microsoft.AspNetCore.Authorization;
-
-namespace Umbraco.Cms.Web.BackOffice.Authorization;
-
-///
-/// Authorization requirement for the
-///
-public class UserGroupRequirement : IAuthorizationRequirement
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// Query string name from which to authorize values.
- public UserGroupRequirement(string queryStringName = "id") => QueryStringName = queryStringName;
-
- ///
- /// Gets the query string name from which to authorize values.
- ///
- public string QueryStringName { get; }
-}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/AnalyticsController.cs b/src/Umbraco.Web.BackOffice/Controllers/AnalyticsController.cs
deleted file mode 100644
index b9980308b9..0000000000
--- a/src/Umbraco.Web.BackOffice/Controllers/AnalyticsController.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using Umbraco.Cms.Core.Models;
-using Umbraco.Cms.Core.Services;
-using Umbraco.Cms.Web.Common.Authorization;
-
-namespace Umbraco.Cms.Web.BackOffice.Controllers;
-
-[Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)]
-public class AnalyticsController : UmbracoAuthorizedJsonController
-{
- private readonly IMetricsConsentService _metricsConsentService;
-
- public AnalyticsController(IMetricsConsentService metricsConsentService) =>
- _metricsConsentService = metricsConsentService;
-
- public TelemetryLevel GetConsentLevel() => _metricsConsentService.GetConsentLevel();
-
- [HttpPost]
- public IActionResult SetConsentLevel([FromBody] TelemetryResource telemetryResource)
- {
- if (!ModelState.IsValid)
- {
- return BadRequest();
- }
-
- _metricsConsentService.SetConsentLevel(telemetryResource.TelemetryLevel);
- return Ok();
- }
-
- public IEnumerable GetAllLevels() =>
- new[] { TelemetryLevel.Minimal, TelemetryLevel.Basic, TelemetryLevel.Detailed };
-}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
deleted file mode 100644
index 45a1746b7e..0000000000
--- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
+++ /dev/null
@@ -1,772 +0,0 @@
-using System.Globalization;
-using System.Security.Claims;
-using System.Security.Cryptography;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Identity;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Routing;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using Umbraco.Cms.Core;
-using Umbraco.Cms.Core.Configuration.Models;
-using Umbraco.Cms.Core.Hosting;
-using Umbraco.Cms.Core.Mail;
-using Umbraco.Cms.Core.Mapping;
-using Umbraco.Cms.Core.Models;
-using Umbraco.Cms.Core.Models.ContentEditing;
-using Umbraco.Cms.Core.Models.Email;
-using Umbraco.Cms.Core.Models.Membership;
-using Umbraco.Cms.Core.Net;
-using Umbraco.Cms.Core.Security;
-using Umbraco.Cms.Core.Services;
-using Umbraco.Cms.Infrastructure.Security;
-using Umbraco.Cms.Web.BackOffice.Filters;
-using Umbraco.Cms.Web.BackOffice.Security;
-using Umbraco.Cms.Web.Common.ActionsResults;
-using Umbraco.Cms.Web.Common.Attributes;
-using Umbraco.Cms.Web.Common.Authorization;
-using Umbraco.Cms.Web.Common.Controllers;
-using Umbraco.Cms.Web.Common.Filters;
-using Umbraco.Cms.Web.Common.Models;
-using Umbraco.Extensions;
-using SignInResult = Microsoft.AspNetCore.Identity.SignInResult;
-
-namespace Umbraco.Cms.Web.BackOffice.Controllers;
-// See
-// for a bigger example of this type of controller implementation in netcore:
-// https://github.com/dotnet/AspNetCore.Docs/blob/2efb4554f8f659be97ee7cd5dd6143b871b330a5/aspnetcore/migration/1x-to-2x/samples/AspNetCoreDotNetCore2App/AspNetCoreDotNetCore2App/Controllers/AccountController.cs
-// https://github.com/dotnet/AspNetCore.Docs/blob/ad16f5e1da6c04fa4996ee67b513f2a90fa0d712/aspnetcore/common/samples/WebApplication1/Controllers/AccountController.cs
-// with authenticator app
-// https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/security/authentication/identity/sample/src/ASPNETCore-IdentityDemoComplete/IdentityDemo/Controllers/AccountController.cs
-
-[PluginController(Constants.Web.Mvc
- .BackOfficeApiArea)] // TODO: Maybe this could be applied with our Application Model conventions
-//[ValidationFilter] // TODO: I don't actually think this is required with our custom Application Model conventions applied
-[AngularJsonOnlyConfiguration] // TODO: This could be applied with our Application Model conventions
-[IsBackOffice]
-[DisableBrowserCache]
-public class AuthenticationController : UmbracoApiControllerBase
-{
- // NOTE: Each action must either be explicitly authorized or explicitly [AllowAnonymous], the latter is optional because
- // this controller itself doesn't require authz but it's more clear what the intention is.
-
- private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
- private readonly IBackOfficeTwoFactorOptions _backOfficeTwoFactorOptions;
- private readonly IEmailSender _emailSender;
- private readonly IBackOfficeExternalLoginProviders _externalAuthenticationOptions;
- private readonly GlobalSettings _globalSettings;
- private readonly IHostingEnvironment _hostingEnvironment;
- private readonly IHttpContextAccessor _httpContextAccessor;
- private readonly IIpResolver _ipResolver;
- private readonly LinkGenerator _linkGenerator;
- private readonly ILogger _logger;
- private readonly UserPasswordConfigurationSettings _passwordConfiguration;
- private readonly SecuritySettings _securitySettings;
- private readonly IBackOfficeSignInManager _signInManager;
- private readonly ISmsSender _smsSender;
- private readonly ILocalizedTextService _textService;
- private readonly ITwoFactorLoginService _twoFactorLoginService;
- private readonly IUmbracoMapper _umbracoMapper;
- private readonly IBackOfficeUserManager _userManager;
- private readonly IUserService _userService;
- private readonly WebRoutingSettings _webRoutingSettings;
-
- // TODO: We need to review all _userManager.Raise calls since many/most should be on the usermanager or signinmanager, very few should be here
- [ActivatorUtilitiesConstructor]
- public AuthenticationController(
- IBackOfficeSecurityAccessor backofficeSecurityAccessor,
- IBackOfficeUserManager backOfficeUserManager,
- IBackOfficeSignInManager signInManager,
- IUserService userService,
- ILocalizedTextService textService,
- IUmbracoMapper umbracoMapper,
- IOptionsSnapshot globalSettings,
- IOptionsSnapshot securitySettings,
- ILogger logger,
- IIpResolver ipResolver,
- IOptionsSnapshot passwordConfiguration,
- IEmailSender emailSender,
- ISmsSender smsSender,
- IHostingEnvironment hostingEnvironment,
- LinkGenerator linkGenerator,
- IBackOfficeExternalLoginProviders externalAuthenticationOptions,
- IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions,
- IHttpContextAccessor httpContextAccessor,
- IOptions webRoutingSettings,
- ITwoFactorLoginService twoFactorLoginService)
- {
- _backofficeSecurityAccessor = backofficeSecurityAccessor;
- _userManager = backOfficeUserManager;
- _signInManager = signInManager;
- _userService = userService;
- _textService = textService;
- _umbracoMapper = umbracoMapper;
- _globalSettings = globalSettings.Value;
- _securitySettings = securitySettings.Value;
- _logger = logger;
- _ipResolver = ipResolver;
- _passwordConfiguration = passwordConfiguration.Value;
- _emailSender = emailSender;
- _smsSender = smsSender;
- _hostingEnvironment = hostingEnvironment;
- _linkGenerator = linkGenerator;
- _externalAuthenticationOptions = externalAuthenticationOptions;
- _backOfficeTwoFactorOptions = backOfficeTwoFactorOptions;
- _httpContextAccessor = httpContextAccessor;
- _webRoutingSettings = webRoutingSettings.Value;
- _twoFactorLoginService = twoFactorLoginService;
- }
-
- ///
- /// Returns the configuration for the backoffice user membership provider - used to configure the change password
- /// dialog
- ///
- [AllowAnonymous] // Needed for users that are invited when they use the link from the mail they are not authorized
- [Authorize(Policy =
- AuthorizationPolicies.BackOfficeAccess)] // Needed to enforce the principle set on the request, if one exists.
- public IDictionary GetPasswordConfig(int userId)
- {
- Attempt currentUserId =
- _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId() ?? Attempt.Fail();
- return _passwordConfiguration.GetConfiguration(
- currentUserId.Success
- ? currentUserId.Result != userId
- : true);
- }
-
- ///
- /// Checks if a valid token is specified for an invited user and if so logs the user in and returns the user object
- ///
- ///
- ///
- ///
- ///
- /// This will also update the security stamp for the user so it can only be used once
- ///
- [ValidateAngularAntiForgeryToken]
- [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)]
- public async Task> PostVerifyInvite([FromQuery] int id, [FromQuery] string token)
- {
- if (string.IsNullOrWhiteSpace(token))
- {
- return NotFound();
- }
-
- var decoded = token.FromUrlBase64();
- if (decoded.IsNullOrWhiteSpace())
- {
- return NotFound();
- }
-
- BackOfficeIdentityUser? identityUser = await _userManager.FindByIdAsync(id.ToString());
- if (identityUser == null)
- {
- return NotFound();
- }
-
- IdentityResult result = await _userManager.ConfirmEmailAsync(identityUser, decoded!);
-
- if (result.Succeeded == false)
- {
- return ValidationErrorResult.CreateNotificationValidationErrorResult(result.Errors.ToErrorMessage());
- }
-
- await _signInManager.SignOutAsync();
-
- await _signInManager.SignInAsync(identityUser, false);
-
- IUser? user = _userService.GetUserById(id);
-
- return _umbracoMapper.Map(user);
- }
-
- [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)]
- [ValidateAngularAntiForgeryToken]
- public async Task PostUnLinkLogin(UnLinkLoginModel unlinkLoginModel)
- {
- var userId = User.Identity?.GetUserId();
- if (userId is null)
- {
- 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);
-
- if (authType == null)
- {
- _logger.LogWarning("Could not find external authentication provider registered: {LoginProvider}", unlinkLoginModel.LoginProvider);
- }
- else
- {
- BackOfficeExternaLoginProviderScheme? opt = await _externalAuthenticationOptions.GetAsync(authType.Name);
- if (opt == null)
- {
- return BadRequest(
- $"Could not find external authentication options registered for provider {unlinkLoginModel.LoginProvider}");
- }
-
- if (!opt.ExternalLoginProvider.Options.AutoLinkOptions.AllowManualLinking)
- {
- // If AllowManualLinking is disabled for this provider we cannot unlink
- return BadRequest();
- }
- }
-
- IdentityResult result = await _userManager.RemoveLoginAsync(
- user,
- unlinkLoginModel.LoginProvider,
- unlinkLoginModel.ProviderKey);
-
- if (result.Succeeded)
- {
- await _signInManager.SignInAsync(user, true);
- return Ok();
- }
-
- AddModelErrors(result);
- return new ValidationErrorResult(ModelState);
- }
-
- [HttpGet]
- [AllowAnonymous]
- public async Task GetRemainingTimeoutSeconds()
- {
- // force authentication to occur since this is not an authorized endpoint
- AuthenticateResult result = await this.AuthenticateBackOfficeAsync();
- if (!result.Succeeded)
- {
- return 0;
- }
-
- var remainingSeconds = result.Principal.GetRemainingAuthSeconds();
- if (remainingSeconds <= 30)
- {
- var username = result.Principal.FindFirst(ClaimTypes.Name)?.Value;
-
- //NOTE: We are using 30 seconds because that is what is coded into angular to force logout to give some headway in
- // the timeout process.
-
- _logger.LogInformation(
- "User logged will be logged out due to timeout: {Username}, IP Address: {IPAddress}",
- username ?? "unknown",
- _ipResolver.GetCurrentRequestIpAddress());
- }
-
- return remainingSeconds;
- }
-
- ///
- /// Checks if the current user's cookie is valid and if so returns OK or a 400 (BadRequest)
- ///
- ///
- [HttpGet]
- [AllowAnonymous]
- public async Task IsAuthenticated()
- {
- // force authentication to occur since this is not an authorized endpoint
- AuthenticateResult result = await this.AuthenticateBackOfficeAsync();
- return result.Succeeded;
- }
-
- ///
- /// Returns the currently logged in Umbraco user
- ///
- ///
- ///
- /// We have the attribute [SetAngularAntiForgeryTokens] applied because this method is called initially to determine if
- /// the user
- /// is valid before the login screen is displayed. The Auth cookie can be persisted for up to a day but the csrf
- /// cookies are only session
- /// cookies which means that the auth cookie could be valid but the csrf cookies are no longer there, in that case we
- /// need to re-set the csrf cookies.
- ///
- [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)]
- [SetAngularAntiForgeryTokens]
- [CheckIfUserTicketDataIsStale]
- public UserDetail? GetCurrentUser()
- {
- IUser? user = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser;
- UserDetail? result = _umbracoMapper.Map(user);
-
- if (result is not null)
- {
- //set their remaining seconds
- result.SecondsUntilTimeout = HttpContext.User.GetRemainingAuthSeconds();
- }
-
- return result;
- }
-
- ///
- /// When a user is invited they are not approved but we need to resolve the partially logged on (non approved)
- /// user.
- ///
- /// It returns a 403 error if the logged-in user has already been created.
- ///
- /// We cannot user GetCurrentUser since that requires they are approved, this is the same as GetCurrentUser but doesn't
- /// require them to be approved
- ///
- [Authorize(Policy = AuthorizationPolicies.BackOfficeAccessWithoutApproval)]
- [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)]
- [SetAngularAntiForgeryTokens]
- [AllowAnonymous] // Needed for users that are invited when they use the link from the mail they may have logged in on a different session, so we don't want to redirect them.
- public ActionResult GetCurrentInvitedUser()
- {
- IUser? user = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser;
-
- if (user?.IsApproved ?? false)
- {
- // if they are approved, than they are no longer invited and we can return an error
- return Forbid();
- }
-
- UserDetail? result = _umbracoMapper.Map(user);
-
- if (result is not null)
- {
- // set their remaining seconds
- result.SecondsUntilTimeout = HttpContext.User.GetRemainingAuthSeconds();
- }
-
- return result;
- }
-
- ///
- /// When a user is invited and they click on the invitation link, they will be partially logged in
- /// where they can set their username/password.
- ///
- /// The model for the new password.
- /// The user model for the invited user.
- ///
- /// This only works when the user is logged in (partially).
- ///
- [Authorize(Policy = AuthorizationPolicies.BackOfficeAccessWithoutApproval)]
- [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)]
- [SetAngularAntiForgeryTokens]
- [AllowAnonymous] // Needed for users that are invited when they use the link from the mail they may have logged in on a different session, so we don't want to redirect them.
- public async Task> PostSetInvitedUserPassword(InvitePasswordModel invitePasswordModel)
- {
- IUser? currentUser = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser;
-
- if (currentUser is null)
- {
- return BadRequest("Could not find user");
- }
-
- if (currentUser.IsApproved)
- {
- // if they are approved, than they are no longer invited and we can return an error
- return Forbid();
- }
-
- BackOfficeIdentityUser? user = await _userManager.FindByIdAsync(currentUser!.Id.ToString());
-
- if (user is null)
- {
- return BadRequest("Could not find identity user");
- }
-
- IdentityResult result = await _userManager.AddPasswordAsync(user, invitePasswordModel.NewPassword);
-
- if (result.Succeeded is false)
- {
- // it wasn't successful, so add the change error to the model state, we've name the property alias _umb_password on the form
- // so that is why it is being used here.
- ModelState.AddModelError("value", result.Errors.ToErrorMessage());
-
- return ValidationProblem(ModelState);
- }
-
- if (_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser is not null)
- {
- // They've successfully set their password, we can now update their user account to be approved
- _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.IsApproved = true;
-
- // They've successfully set their password, and will now get fully logged into the back office, so the lastlogindate is set so the backoffice shows they have logged in
- _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.LastLoginDate = DateTime.UtcNow;
-
- _userService.Save(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser);
- }
-
-
- // now we can return their full object since they are now really logged into the back office
- UserDetail? userDisplay =
- _umbracoMapper.Map(_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser);
-
- if (userDisplay is not null)
- {
- userDisplay.SecondsUntilTimeout = HttpContext.User.GetRemainingAuthSeconds();
- }
-
- return userDisplay;
- }
-
- ///
- /// Logs a user in
- ///
- ///
- [SetAngularAntiForgeryTokens]
- [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)]
- public async Task> PostLogin(LoginModel loginModel)
- {
- // Sign the user in with username/password, this also gives a chance for developers to
- // custom verify the credentials and auto-link user accounts with a custom IBackOfficePasswordChecker
- SignInResult result = await _signInManager.PasswordSignInAsync(
- loginModel.Username, loginModel.Password, true, true);
-
- if (result.Succeeded)
- {
- // return the user detail
- return GetUserDetail(_userService.GetByUsername(loginModel.Username));
- }
-
- if (result.RequiresTwoFactor)
- {
- var twofactorView = _backOfficeTwoFactorOptions.GetTwoFactorView(loginModel.Username);
-
- IUser? attemptedUser = _userService.GetByUsername(loginModel.Username);
-
- // create a with information to display a custom two factor send code view
- var verifyResponse =
- new ObjectResult(new { twoFactorView = twofactorView, userId = attemptedUser?.Id })
- {
- StatusCode = StatusCodes.Status402PaymentRequired
- };
-
- return verifyResponse;
- }
-
- // TODO: We can check for these and respond differently if we think it's important
- // result.IsLockedOut
- // result.IsNotAllowed
-
- // return BadRequest (400), we don't want to return a 401 because that get's intercepted
- // by our angular helper because it thinks that we need to re-perform the request once we are
- // authorized and we don't want to return a 403 because angular will show a warning message indicating
- // that the user doesn't have access to perform this function, we just want to return a normal invalid message.
- return BadRequest();
- }
-
- ///
- /// Processes a password reset request. Looks for a match on the provided email address
- /// and if found sends an email with a link to reset it
- ///
- ///
- [SetAngularAntiForgeryTokens]
- [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)]
- public async Task PostRequestPasswordReset(RequestPasswordResetModel model)
- {
- // If this feature is switched off in configuration the UI will be amended to not make the request to reset password available.
- // So this is just a server-side secondary check.
- if (_securitySettings.AllowPasswordReset == false)
- {
- return BadRequest();
- }
-
- BackOfficeIdentityUser? identityUser = await _userManager.FindByEmailAsync(model.Email);
-
- await Task.Delay(RandomNumberGenerator.GetInt32(400, 2500)); // To randomize response time preventing user enumeration
-
- if (identityUser != null)
- {
- IUser? user = _userService.GetByEmail(model.Email);
- if (user != null)
- {
- var from = _globalSettings.Smtp?.From;
- var code = await _userManager.GeneratePasswordResetTokenAsync(identityUser);
- var callbackUrl = ConstructCallbackUrl(identityUser.Id, code);
-
- var message = _textService.Localize("login", "resetPasswordEmailCopyFormat",
- // Ensure the culture of the found user is used for the email!
- UmbracoUserExtensions.GetUserCulture(identityUser.Culture, _textService, _globalSettings),
- new[] { identityUser.UserName, callbackUrl });
-
- var subject = _textService.Localize("login", "resetPasswordEmailCopySubject",
- // Ensure the culture of the found user is used for the email!
- UmbracoUserExtensions.GetUserCulture(identityUser.Culture, _textService, _globalSettings));
-
- var mailMessage = new EmailMessage(from, user.Email, subject, message, true);
-
- try
- {
- await _emailSender.SendAsync(mailMessage, Constants.Web.EmailTypes.PasswordReset, true);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error sending email, please check your SMTP configuration: {ErrorMessage}", ex.Message);
- return Ok();
- }
-
- _userManager.NotifyForgotPasswordRequested(User, user.Id.ToString());
- }
- }
-
- return Ok();
- }
-
- ///
- /// Used to retrieve the 2FA providers for code submission
- ///
- ///
- [SetAngularAntiForgeryTokens]
- [AllowAnonymous]
- public async Task>> Get2FAProviders()
- {
- BackOfficeIdentityUser? user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
- if (user == null)
- {
- _logger.LogWarning("Get2FAProviders :: No verified user found, returning 404");
- return NotFound();
- }
-
- IEnumerable userFactors = await _twoFactorLoginService.GetEnabledTwoFactorProviderNamesAsync(user.Key);
-
- return new ObjectResult(userFactors);
- }
-
- [SetAngularAntiForgeryTokens]
- [AllowAnonymous]
- public async Task PostSend2FACode([FromBody] string provider)
- {
- if (provider.IsNullOrWhiteSpace())
- {
- return NotFound();
- }
-
- BackOfficeIdentityUser? user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
- if (user == null)
- {
- _logger.LogWarning("PostSend2FACode :: No verified user found, returning 404");
- return NotFound();
- }
-
- var from = _globalSettings.Smtp?.From;
- // Generate the token and send it
- var code = await _userManager.GenerateTwoFactorTokenAsync(user, provider);
- if (string.IsNullOrWhiteSpace(code))
- {
- _logger.LogWarning("PostSend2FACode :: Could not generate 2FA code");
- return BadRequest("Invalid code");
- }
-
- var subject = _textService.Localize("login", "mfaSecurityCodeSubject",
- // Ensure the culture of the found user is used for the email!
- UmbracoUserExtensions.GetUserCulture(user.Culture, _textService, _globalSettings));
-
- var message = _textService.Localize("login", "mfaSecurityCodeMessage",
- // Ensure the culture of the found user is used for the email!
- 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")
- {
- var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
- if (phoneNumber is not null)
- {
- await _smsSender.SendSmsAsync(phoneNumber, message);
- }
- }
-
- return Ok();
- }
-
- [SetAngularAntiForgeryTokens]
- [AllowAnonymous]
- public async Task> PostVerify2FACode(Verify2FACodeModel model)
- {
- if (ModelState.IsValid == false)
- {
- return new ValidationErrorResult(ModelState);
- }
-
- BackOfficeIdentityUser? user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
- if (user == null)
- {
- _logger.LogWarning("PostVerify2FACode :: No verified user found, returning 404");
- return NotFound();
- }
-
- SignInResult result =
- await _signInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.IsPersistent, model.RememberClient);
- if (result.Succeeded)
- {
- return Ok(GetUserDetail(_userService.GetByUsername(user.UserName)));
- }
-
- if (result.IsLockedOut)
- {
- return new ValidationErrorResult("User is locked out");
- }
-
- if (result.IsNotAllowed)
- {
- return new ValidationErrorResult("User is not allowed");
- }
-
- return new ValidationErrorResult("Invalid code");
- }
-
- ///
- /// Processes a set password request. Validates the request and sets a new password.
- ///
- ///
- [SetAngularAntiForgeryTokens]
- [AllowAnonymous]
- public async Task PostSetPassword(SetPasswordModel model)
- {
- 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)
- {
- var lockedOut = await _userManager.IsLockedOutAsync(identityUser);
- if (lockedOut)
- {
- _logger.LogInformation(
- "User {UserId} is currently locked out, unlocking and resetting AccessFailedCount", model.UserId);
-
- //// var user = await UserManager.FindByIdAsync(model.UserId);
- IdentityResult unlockResult =
- await _userManager.SetLockoutEndDateAsync(identityUser, DateTimeOffset.Now);
- if (unlockResult.Succeeded == false)
- {
- _logger.LogWarning("Could not unlock for user {UserId} - error {UnlockError}", model.UserId,
- unlockResult.Errors.First().Description);
- }
-
- IdentityResult resetAccessFailedCountResult =
- await _userManager.ResetAccessFailedCountAsync(identityUser);
- if (resetAccessFailedCountResult.Succeeded == false)
- {
- _logger.LogWarning("Could not reset access failed count {UserId} - error {UnlockError}",
- model.UserId, unlockResult.Errors.First().Description);
- }
- }
-
- // They've successfully set their password, we can now update their user account to be confirmed
- // if user was only invited, then they have not been approved
- // but a successful forgot password flow (e.g. if their token had expired and they did a forgot password instead of request new invite)
- // means we have verified their email
- if (!await _userManager.IsEmailConfirmedAsync(identityUser))
- {
- await _userManager.ConfirmEmailAsync(identityUser, model.ResetCode);
- }
-
- // invited is not approved, never logged in, invited date present
- /*
- if (LastLoginDate == default && IsApproved == false && InvitedDate != null)
- return UserState.Invited;
- */
- if (identityUser != null && !identityUser.IsApproved)
- {
- IUser? user = _userService.GetByUsername(identityUser.UserName);
- // also check InvitedDate and never logged in, otherwise this would allow a disabled user to reactivate their account with a forgot password
- if (user?.LastLoginDate == default && user?.InvitedDate != null)
- {
- user.IsApproved = true;
- user.InvitedDate = null;
- _userService.Save(user);
- }
- }
-
- _userManager.NotifyForgotPasswordChanged(User, model.UserId.ToString(CultureInfo.InvariantCulture));
- return Ok();
- }
-
- return new ValidationErrorResult(
- result.Errors.Any() ? result.Errors.First().Description : "Set password failed");
- }
-
- ///
- /// Logs the current user out
- ///
- ///
- [ValidateAngularAntiForgeryToken]
- [AllowAnonymous]
- public async Task PostLogout()
- {
- // force authentication to occur since this is not an authorized endpoint
- AuthenticateResult result = await this.AuthenticateBackOfficeAsync();
- if (!result.Succeeded)
- {
- return Ok();
- }
-
- await _signInManager.SignOutAsync();
-
- _logger.LogInformation("User {UserName} from IP address {RemoteIpAddress} has logged out",
- result.Principal.Identity == null ? "UNKNOWN" : result.Principal.Identity.Name, HttpContext.Connection.RemoteIpAddress);
-
- var userId = result.Principal.Identity?.GetUserId();
- SignOutSuccessResult args = _userManager.NotifyLogoutSuccess(User, userId);
- if (!args.SignOutRedirectUrl.IsNullOrWhiteSpace())
- {
- return new ObjectResult(new { signOutRedirectUrl = args.SignOutRedirectUrl });
- }
-
- return Ok();
- }
-
-
- ///
- /// Return the for the given
- ///
- ///
- ///
- private UserDetail? GetUserDetail(IUser? user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- UserDetail? userDetail = _umbracoMapper.Map(user);
-
- if (userDetail is not null)
- {
- // update the userDetail and set their remaining seconds
- userDetail.SecondsUntilTimeout = _globalSettings.TimeOut.TotalSeconds;
- }
-
- return userDetail;
- }
-
- private string ConstructCallbackUrl(string userId, string code)
- {
- // Get an mvc helper to get the url
- var action = _linkGenerator.GetPathByAction(
- nameof(BackOfficeController.ValidatePasswordResetCode),
- ControllerExtensions.GetControllerName(),
- new { area = Constants.Web.Mvc.BackOfficeArea, u = userId, r = code });
-
- // Construct full URL using configured application URL (which will fall back to current request)
- Uri applicationUri = _httpContextAccessor.GetRequiredHttpContext().Request
- .GetApplicationUri(_webRoutingSettings);
- var callbackUri = new Uri(applicationUri, action);
- return callbackUri.ToString();
- }
-
- private void AddModelErrors(IdentityResult result, string prefix = "")
- {
- foreach (IdentityError? error in result.Errors)
- {
- ModelState.AddModelError(prefix, error.Description);
- }
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeAssetsController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeAssetsController.cs
deleted file mode 100644
index b73788fe30..0000000000
--- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeAssetsController.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using Umbraco.Cms.Core;
-using Umbraco.Cms.Core.Configuration.Models;
-using Umbraco.Cms.Core.Hosting;
-using Umbraco.Cms.Core.IO;
-using Umbraco.Cms.Web.Common.Attributes;
-
-namespace Umbraco.Cms.Web.BackOffice.Controllers;
-
-[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
-public class BackOfficeAssetsController : UmbracoAuthorizedJsonController
-{
- private readonly IFileSystem _jsLibFileSystem;
-
- public BackOfficeAssetsController(IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, ILoggerFactory loggerFactory, IOptionsSnapshot globalSettings)
- {
- var path = globalSettings.Value.UmbracoPath + Path.DirectorySeparatorChar + "lib";
- _jsLibFileSystem = new PhysicalFileSystem(
- ioHelper,
- hostingEnvironment,
- loggerFactory.CreateLogger(),
- hostingEnvironment.MapPathWebRoot(path),
- hostingEnvironment.ToAbsolute(path));
- }
-
- [HttpGet]
- public object GetSupportedLocales()
- {
- const string momentLocaleFolder = "moment";
- const string flatpickrLocaleFolder = "flatpickr/l10n";
-
- return new { moment = GetLocales(momentLocaleFolder), flatpickr = GetLocales(flatpickrLocaleFolder) };
- }
-
- private IEnumerable GetLocales(string path)
- {
- var cultures = _jsLibFileSystem.GetFiles(path, "*.js").ToList();
- for (var i = 0; i < cultures.Count; i++)
- {
- cultures[i] = cultures[i].Substring(cultures[i].IndexOf(path, StringComparison.Ordinal) + path.Length + 1);
- }
-
- return cultures;
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs
deleted file mode 100644
index 979691acf2..0000000000
--- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs
+++ /dev/null
@@ -1,630 +0,0 @@
-using System.Globalization;
-using System.Net;
-using System.Security.Claims;
-using System.Security.Principal;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Identity;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using Microsoft.Extensions.Primitives;
-using Umbraco.Cms.Core;
-using Umbraco.Cms.Core.Cache;
-using Umbraco.Cms.Core.Configuration.Models;
-using Umbraco.Cms.Core.Hosting;
-using Umbraco.Cms.Core.Manifest;
-using Umbraco.Cms.Core.Security;
-using Umbraco.Cms.Core.Serialization;
-using Umbraco.Cms.Core.Services;
-using Umbraco.Cms.Core.WebAssets;
-using Umbraco.Cms.Infrastructure.WebAssets;
-using Umbraco.Cms.Web.BackOffice.ActionResults;
-using Umbraco.Cms.Web.BackOffice.Filters;
-using Umbraco.Cms.Web.BackOffice.Install;
-using Umbraco.Cms.Web.BackOffice.Security;
-using Umbraco.Cms.Web.Common.ActionsResults;
-using Umbraco.Cms.Web.Common.Attributes;
-using Umbraco.Cms.Web.Common.Authorization;
-using Umbraco.Cms.Web.Common.Controllers;
-using Umbraco.Cms.Web.Common.Filters;
-using Umbraco.Extensions;
-using SignInResult = Microsoft.AspNetCore.Identity.SignInResult;
-
-namespace Umbraco.Cms.Web.BackOffice.Controllers;
-
-[DisableBrowserCache]
-[UmbracoRequireHttps]
-[PluginController(Constants.Web.Mvc.BackOfficeArea)]
-[IsBackOffice]
-public class BackOfficeController : UmbracoController
-{
- private readonly AppCaches _appCaches;
- private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
- private readonly BackOfficeServerVariables _backOfficeServerVariables;
- private readonly IBackOfficeTwoFactorOptions _backOfficeTwoFactorOptions;
- private readonly IBackOfficeExternalLoginProviders _externalLogins;
- private readonly IHostingEnvironment _hostingEnvironment;
- private readonly IHttpContextAccessor _httpContextAccessor;
- private readonly IJsonSerializer _jsonSerializer;
- private readonly ILogger _logger;
- private readonly IRuntimeMinifier _runtimeMinifier;
- private readonly IRuntimeState _runtimeState;
- private readonly IOptions _securitySettings;
- private readonly ServerVariablesParser _serverVariables;
- private readonly IBackOfficeSignInManager _signInManager;
-
- private readonly ILocalizedTextService _textService;
- // See here for examples of what a lot of this is doing: https://github.com/dotnet/aspnetcore/blob/main/src/Identity/samples/IdentitySample.Mvc/Controllers/AccountController.cs
- // along with our AuthenticationController
-
- // NOTE: Each action must either be explicitly authorized or explicitly [AllowAnonymous], the latter is optional because
- // this controller itself doesn't require authz but it's more clear what the intention is.
-
- private readonly IBackOfficeUserManager _userManager;
- private readonly GlobalSettings _globalSettings;
-
-
- [ActivatorUtilitiesConstructor]
- public BackOfficeController(
- IBackOfficeUserManager userManager,
- IRuntimeState runtimeState,
- IRuntimeMinifier runtimeMinifier,
- IOptionsSnapshot globalSettings,
- IHostingEnvironment hostingEnvironment,
- ILocalizedTextService textService,
- BackOfficeServerVariables backOfficeServerVariables,
- AppCaches appCaches,
- IBackOfficeSignInManager signInManager,
- IBackOfficeSecurityAccessor backofficeSecurityAccessor,
- ILogger logger,
- IJsonSerializer jsonSerializer,
- IBackOfficeExternalLoginProviders externalLogins,
- IHttpContextAccessor httpContextAccessor,
- IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions,
- ServerVariablesParser serverVariables,
- IOptions securitySettings)
- {
- _userManager = userManager;
- _runtimeState = runtimeState;
- _runtimeMinifier = runtimeMinifier;
- _globalSettings = globalSettings.Value;
- _hostingEnvironment = hostingEnvironment;
- _textService = textService;
- _backOfficeServerVariables = backOfficeServerVariables;
- _appCaches = appCaches;
- _signInManager = signInManager;
- _backofficeSecurityAccessor = backofficeSecurityAccessor;
- _logger = logger;
- _jsonSerializer = jsonSerializer;
- _externalLogins = externalLogins;
- _httpContextAccessor = httpContextAccessor;
- _backOfficeTwoFactorOptions = backOfficeTwoFactorOptions;
- _serverVariables = serverVariables;
- _securitySettings = securitySettings;
- }
-
- [HttpGet]
- [AllowAnonymous]
- public async Task Default()
- {
- // force authentication to occur since this is not an authorized endpoint
- AuthenticateResult result = await this.AuthenticateBackOfficeAsync();
-
- /*
- TODO: Remove authentication check & clean controller in V14
- This is crossed out in V14 to allow the Backoffice to handle authentication itself whilst still allowing
- the old Umbraco.Web.UI executable now using the updated login screen from V13 to work with the old Backoffice.
-
- // if we are not authenticated then we need to redirect to the login page
- if (!result.Succeeded)
- {
- return RedirectToLogin(null);
- }
- */
-
- ViewResult defaultView = DefaultView();
-
- return await RenderDefaultOrProcessExternalLoginAsync(
- result,
- () => defaultView);
- }
-
- ///
- /// Returns the default view for the BackOffice
- ///
- /// The default view currently /umbraco/UmbracoBackOffice/Default.cshtml
- public ViewResult DefaultView()
- {
- var viewPath = Path.Combine(Constants.SystemDirectories.Umbraco, Constants.Web.Mvc.BackOfficeArea, nameof(Default) + ".cshtml")
- .Replace("\\", "/"); // convert to forward slashes since it's a virtual path
- return View(viewPath);
- }
-
- [HttpGet]
- [AllowAnonymous]
- public async Task Login()
- {
- // force authentication to occur since this is not an authorized endpoint
- AuthenticateResult result = await this.AuthenticateBackOfficeAsync();
-
- var viewPath = Path.Combine(Constants.SystemDirectories.Umbraco, Constants.Web.Mvc.BackOfficeLoginArea, "Index.cshtml")
- .Replace("\\", "/"); // convert to forward slashes since it's a virtual path
-
- return await RenderDefaultOrProcessExternalLoginAsync(
- result,
- () => View(viewPath));
- }
-
- [HttpGet]
- [AllowAnonymous]
- public async Task VerifyInvite(string invite)
- {
- AuthenticateResult authenticate = await this.AuthenticateBackOfficeAsync();
-
- //if you are hitting VerifyInvite, you're already signed in as a different user, and the token is invalid
- //you'll exit on one of the return RedirectToAction(nameof(Default)) but you're still logged in so you just get
- //dumped at the default admin view with no detail
- if (authenticate.Succeeded)
- {
- await _signInManager.SignOutAsync();
- }
-
- if (invite == null)
- {
- _logger.LogWarning("VerifyUser endpoint reached with invalid token: NULL");
- return RedirectToLogin(new { flow = "invite-user", status = "invalidToken" });
- }
-
- var parts = WebUtility.UrlDecode(invite).Split('|');
-
- if (parts.Length != 2)
- {
- _logger.LogWarning("VerifyUser endpoint reached with invalid token: {Invite}", invite);
- return RedirectToLogin(new { flow = "invite-user", status = "invalidToken" });
- }
-
- var token = parts[1];
-
- var decoded = token.FromUrlBase64();
- if (decoded.IsNullOrWhiteSpace())
- {
- _logger.LogWarning("VerifyUser endpoint reached with invalid token: {Invite}", invite);
- return RedirectToLogin(new { flow = "invite-user", status = "invalidToken" });
- }
-
- var id = parts[0];
-
- BackOfficeIdentityUser? identityUser = await _userManager.FindByIdAsync(id);
- if (identityUser == null)
- {
- _logger.LogWarning("VerifyUser endpoint reached with non existing user: {UserId}", id);
- return RedirectToLogin(new { flow = "invite-user", status = "nonExistingUser" });
- }
-
- IdentityResult result = await _userManager.ConfirmEmailAsync(identityUser, decoded!);
-
- if (result.Succeeded == false)
- {
- _logger.LogWarning("Could not verify email, Error: {Errors}, Token: {Invite}", result.Errors.ToErrorMessage(), invite);
- return RedirectToLogin(new { flow = "invite-user", status = "false", invite = "3" });
- }
-
- // sign the user in
- DateTime? previousLastLoginDate = identityUser.LastLoginDateUtc;
- await _signInManager.SignInAsync(identityUser, false);
-
- // reset the lastlogindate back to previous as the user hasn't actually logged in, to add a flag or similar to BackOfficeSignInManager would be a breaking change
- identityUser.LastLoginDateUtc = previousLastLoginDate;
- await _userManager.UpdateAsync(identityUser);
-
- return RedirectToLogin(new { flow = "invite-user", invite = "1" });
- }
-
- ///
- /// This Action is used by the installer when an upgrade is detected but the admin user is not logged in. We need to
- /// ensure the user is authenticated before the install takes place so we redirect here to show the standard login
- /// screen.
- ///
- ///
- [HttpGet]
- [StatusCodeResult(HttpStatusCode.ServiceUnavailable)]
- [AllowAnonymous]
- public async Task AuthorizeUpgrade()
- {
- // force authentication to occur since this is not an authorized endpoint
- AuthenticateResult result = await this.AuthenticateBackOfficeAsync();
- if (result.Succeeded)
- {
- // Redirect to installer if we're already authorized
- var installerUrl = Url.Action(nameof(InstallController.Index), ControllerExtensions.GetControllerName(), new { area = Cms.Core.Constants.Web.Mvc.InstallArea }) ?? "/";
- return new LocalRedirectResult(installerUrl);
- }
-
- var viewPath = Path.Combine(Constants.SystemDirectories.Umbraco, Constants.Web.Mvc.BackOfficeArea, nameof(Default) + ".cshtml");
-
- return await RenderDefaultOrProcessExternalLoginAsync(
- result,
- //The default view to render when there is no external login info or errors
- () => View(viewPath));
- }
-
- ///
- /// Returns the JavaScript main file including all references found in manifests
- ///
- ///
- [MinifyJavaScriptResult(Order = 0)]
- [HttpGet]
- [AllowAnonymous]
- public async Task Application()
- {
- // Removed in separate PR
- return Ok();
- }
-
- ///
- /// Get the json localized text for a given culture or the culture for the current user
- ///
- ///
- ///
- [HttpGet]
- [AllowAnonymous]
- public async Task>> LocalizedText(string? culture = null)
- {
- CultureInfo? cultureInfo = null;
- if (string.IsNullOrWhiteSpace(culture))
- {
- // Force authentication to occur since this is not an authorized endpoint, we need this to get a user.
- AuthenticateResult authenticationResult = await this.AuthenticateBackOfficeAsync();
- // We have to get the culture from the Identity, we can't rely on thread culture
- // It's entirely likely for a user to have a different culture in the backoffice, than their system.
- IIdentity? user = authenticationResult.Principal?.Identity;
-
- if (authenticationResult.Succeeded && user is not null)
- {
- cultureInfo = user.GetCulture();
- }
-
- cultureInfo ??= CultureInfo.GetCultureInfo(_globalSettings.DefaultUILanguage);
- }
- else
- {
- cultureInfo = CultureInfo.GetCultureInfo(culture);
- }
-
- IDictionary allValues = _textService.GetAllStoredValues(cultureInfo!);
- var pathedValues = allValues.Select(kv =>
- {
- var slashIndex = kv.Key.IndexOf('/');
- var areaAlias = kv.Key[..slashIndex];
- var valueAlias = kv.Key[(slashIndex + 1)..];
- return new { areaAlias, valueAlias, value = kv.Value };
- });
-
- var nestedDictionary = pathedValues
- .GroupBy(pv => pv.areaAlias)
- .ToDictionary(pv => pv.Key, pv =>
- pv.ToDictionary(pve => pve.valueAlias, pve => pve.value));
-
- return nestedDictionary;
- }
-
- ///
- /// Returns the JavaScript object representing the static server variables javascript object
- ///
- [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)]
- [MinifyJavaScriptResult(Order = 1)]
- public async Task ServerVariables()
- {
- // cache the result if debugging is disabled
- var serverVars = await _serverVariables.ParseAsync(await _backOfficeServerVariables.GetServerVariablesAsync());
- var result = _hostingEnvironment.IsDebugMode
- ? serverVars
- : _appCaches.RuntimeCache.GetCacheItem(
- typeof(BackOfficeController) + "ServerVariables",
- () => serverVars,
- new TimeSpan(0, 10, 0));
-
- return new JavaScriptResult(result);
- }
-
- [HttpPost]
- [AllowAnonymous]
- public ActionResult ExternalLogin(string provider, string? redirectUrl = null)
- {
- // Only relative urls are accepted as redirect url
- // We can't simply use Uri.TryCreate with kind Absolute, as in Linux any relative url would be seen as an absolute file uri
- if (redirectUrl == null || !Uri.TryCreate(redirectUrl, UriKind.RelativeOrAbsolute, out Uri? redirectUri) || redirectUri.IsAbsoluteUri)
- {
- redirectUrl = Url.Action(nameof(Default), this.GetControllerName());
- }
-
- // Configures the redirect URL and user identifier for the specified external login
- AuthenticationProperties properties =
- _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
-
- return Challenge(properties, provider);
- }
-
- ///
- /// Called when a user links an external login provider in the back office
- ///
- ///
- ///
- [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)]
- [HttpPost]
- public ActionResult LinkLogin(string provider)
- {
- // Request a redirect to the external login provider to link a login for the current user
- var redirectUrl = Url.Action(nameof(ExternalLinkLoginCallback), this.GetControllerName());
-
- // Configures the redirect URL and user identifier for the specified external login including xsrf data
- AuthenticationProperties properties =
- _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User));
-
- return Challenge(properties, provider);
- }
-
- [HttpGet]
- [AllowAnonymous]
- public async Task ValidatePasswordResetCode([Bind(Prefix = "u")] int userId, [Bind(Prefix = "r")] string resetCode)
- {
- BackOfficeIdentityUser? user = await _userManager.FindByIdAsync(userId.ToString(CultureInfo.InvariantCulture));
- if (user is null)
- {
- return RedirectToLogin(new { flow = "reset-password", status = "userNotFound" });
- }
-
- var result = await _userManager.VerifyUserTokenAsync(user, "Default", "ResetPassword", resetCode);
-
- return result ?
-
- // Redirect to login with userId and resetCode
- RedirectToLogin(new { flow = "reset-password", userId, resetCode }) :
-
- // Redirect to login with error code
- RedirectToLogin(new { flow = "reset-password", status = "resetCodeExpired" });
- }
-
- ///
- /// Callback path when the user initiates a link login request from the back office to the external provider from the
- /// action
- ///
- ///
- /// An example of this is here
- /// https://github.com/dotnet/aspnetcore/blob/main/src/Identity/samples/IdentitySample.Mvc/Controllers/AccountController.cs#L155
- /// which this is based on
- ///
- [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)]
- [HttpGet]
- public async Task ExternalLinkLoginCallback()
- {
- BackOfficeIdentityUser? user = await _userManager.GetUserAsync(User);
- if (user == null)
- {
- // ... this should really not happen
- TempData[ViewDataExtensions.TokenExternalSignInError] = new[] { "Local user does not exist" };
- return RedirectToLogin(new { flow = "external-login", status = "localUserNotFound", logout = "true"});
- }
-
- ExternalLoginInfo? info =
- await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user));
-
- if (info == null)
- {
- // Add error and redirect for it to be displayed
- TempData[ViewDataExtensions.TokenExternalSignInError] =
- new[] { "An error occurred, could not get external login info" };
- return RedirectToLogin(new { flow = "external-login", status = "externalLoginInfoNotFound", logout = "true"});
- }
-
- IdentityResult addLoginResult = await _userManager.AddLoginAsync(user, info);
- if (addLoginResult.Succeeded)
- {
- // Update any authentication tokens if succeeded
- await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
-
- return RedirectToLocal(Url.Action(nameof(Default), this.GetControllerName()));
- }
-
- // Add errors and redirect for it to be displayed
- TempData[ViewDataExtensions.TokenExternalSignInError] = addLoginResult.Errors;
- return RedirectToLogin(new { flow = "external-login", status = "failed", logout = "true" });
- }
-
- ///
- /// Used by Default and AuthorizeUpgrade to render as per normal if there's no external login info,
- /// otherwise process the external login info.
- ///
- ///
- private async Task RenderDefaultOrProcessExternalLoginAsync(
- AuthenticateResult authenticateResult,
- Func