V14: Remove old backoffice project. (#15752)
* Move magical route to management api * Move auth around * Remove "New" cookies, as they are no longer needed * Move all installer related * Remove BackOfficeServerVariables.cs and trees * Move webhooks to management api * Remove remainting controllers * Remove last services * Move preview to management api * Remove mroe extensions * Remove tours * Remove old Auth handlers * Remove server variables entirely * Remove old backoffice controller * Remove controllers namespace entirely * Move rest of preview * move last services * Move language file extension * Remove old backoffice entirely (Backoffice and Web.UI projects) * Clean up unused security classes * Fix up installer route * Remove obsolete tests * Fix up DI in integration test * Add missing property mapping * Move core mapping into core * Add composers to integration test * remove identity * Fix up DI * Outcomment failing test :) * Fix up remaining test * Update mapper * Remove the actual project files * Remove backoffice cs proj * Remove old backoffice from yml * Run belissima before login * Remove caching * Refactor file paths * Remove belle from static assets * Dont refer to old project in templates * update gitignore * Add missing files * Remove install view as its no longer used * Fix up failing test * Remove outcommented code * Update submodule to latest * fix build --------- Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -182,7 +183,7 @@ public class BackOfficeController : SecurityControllerBase
|
||||
/// </summary>
|
||||
private async Task<string?> 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);
|
||||
}
|
||||
|
||||
@@ -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<IActionResult> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the default view for the BackOffice
|
||||
/// </summary>
|
||||
/// <returns>The default view currently /umbraco/UmbracoBackOffice/Default.cshtml</returns>
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<UserSavingNotification, RevokeUserAuthenticationTokensNotificationHandler>();
|
||||
builder.AddNotificationAsyncHandler<UserSavedNotification, RevokeUserAuthenticationTokensNotificationHandler>();
|
||||
@@ -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;
|
||||
|
||||
@@ -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<IInstallService, InstallService>();
|
||||
|
||||
builder.AddNotificationAsyncHandler<UnattendedInstallNotification, CreateUnattendedUserNotificationHandler>();
|
||||
builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>().Add<InstallerViewModelsMapDefinition>();
|
||||
|
||||
return builder;
|
||||
|
||||
@@ -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<PreviewRoutes>();
|
||||
builder.AddNotificationAsyncHandler<ContentCacheRefresherNotification, PreviewHubUpdater>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="IUmbracoBuilder"/> for the Umbraco back office
|
||||
/// </summary>
|
||||
public static partial class UmbracoBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds all required components to run the Umbraco back office
|
||||
/// </summary>
|
||||
public static IUmbracoBuilder
|
||||
AddBackOffice(this IUmbracoBuilder builder, Action<IMvcBuilder>? 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<IBackOfficePathGenerator, UmbracoBackOfficePathGenerator>();
|
||||
builder.Services.AddUnique<IPhysicalFileSystem>(factory =>
|
||||
{
|
||||
var path = "~/";
|
||||
IHostingEnvironment hostingEnvironment = factory.GetRequiredService<IHostingEnvironment>();
|
||||
return new PhysicalFileSystem(
|
||||
factory.GetRequiredService<IIOHelper>(),
|
||||
hostingEnvironment,
|
||||
factory.GetRequiredService<ILogger<PhysicalFileSystem>>(),
|
||||
hostingEnvironment.MapPathContentRoot(path),
|
||||
hostingEnvironment.ToAbsolute(path)
|
||||
);
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -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<ICoreBackOfficeUserManager, BackOfficeUserManager>();
|
||||
services.AddScoped<IInviteUriProvider, InviteUriProvider>();
|
||||
services.AddScoped<IForgotPasswordUriProvider, ForgotPasswordUriProvider>();
|
||||
services.AddScoped<IBackOfficePasswordChanger, BackOfficePasswordChanger>();
|
||||
|
||||
services.AddSingleton<IBackOfficeUserPasswordChecker, NoopBackOfficeUserPasswordChecker>(); ;
|
||||
services.AddSingleton<IBackOfficeUserPasswordChecker, NoopBackOfficeUserPasswordChecker>();
|
||||
|
||||
// Configure the options specifically for the UmbracoBackOfficeIdentityOptions instance
|
||||
services.ConfigureOptions<ConfigureBackOfficeIdentityOptions>();
|
||||
services.ConfigureOptions<ConfigureBackOfficeSecurityStampValidatorOptions>();
|
||||
|
||||
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;
|
||||
|
||||
@@ -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<BackOfficeAreaRoutes>();
|
||||
builder.Services.AddSingleton<BackOfficeExternalLoginProviderErrorMiddleware>();
|
||||
builder.Services.AddUnique<IConflictingRouteService, ConflictingRouteService>();
|
||||
|
||||
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<ConfigureApiBehaviorOptions>()
|
||||
|
||||
@@ -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<MapDefinitionCollectionBuilder>().Add<WebhookMapDefinition>();
|
||||
builder.Services.AddUnique<IWebhookPresentationFactory, WebhookPresentationFactory>();
|
||||
|
||||
return builder;
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Outputs a script tag containing the import map for the BackOffice.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
/// <returns>A <see cref="Task"/> containing the html content for the BackOffice import map.</returns>
|
||||
public static async Task<IHtmlContent> BackOfficeImportMapScriptAsync(
|
||||
this IHtmlHelper html,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IBackOfficePathGenerator backOfficePathGenerator,
|
||||
IPackageManifestService packageManifestService)
|
||||
{
|
||||
try
|
||||
{
|
||||
PackageManifestImportmap packageImports = await packageManifestService.GetPackageManifestImportmapAsync();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("""<script type="importmap">""");
|
||||
sb.AppendLine(jsonSerializer.Serialize(packageImports));
|
||||
sb.AppendLine("</script>");
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// <returns></returns>
|
||||
public static IUmbracoApplicationBuilderContext UseBackOffice(this IUmbracoApplicationBuilderContext builder)
|
||||
{
|
||||
KeepAliveSettings keepAliveSettings =
|
||||
builder.ApplicationServices.GetRequiredService<IOptions<KeepAliveSettings>>().Value;
|
||||
IHostingEnvironment hostingEnvironment = builder.ApplicationServices.GetRequiredService<IHostingEnvironment>();
|
||||
builder.AppBuilder.Map(
|
||||
hostingEnvironment.ToAbsolute(keepAliveSettings.KeepAlivePingUrl),
|
||||
a => a.UseMiddleware<KeepAliveMiddleware>());
|
||||
|
||||
builder.AppBuilder.UseMiddleware<BackOfficeExternalLoginProviderErrorMiddleware>();
|
||||
return builder;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<UnattendedInstallNotification>
|
||||
{
|
||||
@@ -18,8 +18,7 @@ public class CreateUnattendedUserNotificationHandler : INotificationAsyncHandler
|
||||
private readonly IOptions<UnattendedSettings> _unattendedSettings;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public CreateUnattendedUserNotificationHandler(IOptions<UnattendedSettings> unattendedSettings,
|
||||
IUserService userService, IServiceScopeFactory serviceScopeFactory)
|
||||
public CreateUnattendedUserNotificationHandler(IOptions<UnattendedSettings> unattendedSettings, IUserService userService, IServiceScopeFactory serviceScopeFactory)
|
||||
{
|
||||
_unattendedSettings = unattendedSettings;
|
||||
_userService = userService;
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<IWebhookEvent, WebhookEventResponseModel>((_, _) => new WebhookEventResponseModel(), Map);
|
||||
mapper.Define<WebhookLog, WebhookLogViewModel>((_, _) => new WebhookLogViewModel(), Map);
|
||||
mapper.Define<CreateWebhookRequestModel, IWebhook>((_, _) => new Webhook(string.Empty), Map);
|
||||
mapper.Define<UpdateWebhookRequestModel, IWebhook>((_, _) => 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Used to handle errors registered by external login providers
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Logs any unhandled exception.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Umbraco.Cms.Web.BackOffice.SignalR;
|
||||
namespace Umbraco.Cms.Api.Management.Preview;
|
||||
|
||||
public interface IPreviewHub
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.SignalR;
|
||||
namespace Umbraco.Cms.Api.Management.Preview;
|
||||
|
||||
public class PreviewHub : Hub<IPreviewHub>
|
||||
{
|
||||
@@ -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<ContentCacheRefresherNotification>
|
||||
{
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Creates routes for the back office area
|
||||
@@ -68,27 +67,24 @@ public sealed class BackOfficeAreaRoutes : IAreaRoutes
|
||||
/// </summary>
|
||||
private void MapMinimalBackOffice(IEndpointRouteBuilder endpoints)
|
||||
{
|
||||
endpoints.MapUmbracoRoute<BackOfficeController>(
|
||||
endpoints.MapUmbracoRoute<BackOfficeDefaultController>(
|
||||
_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<AuthenticationController>(_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<BackOfficeController>(),
|
||||
Action = nameof(BackOfficeController.Default)
|
||||
Controller = ControllerExtensions.GetControllerName<BackOfficeDefaultController>(),
|
||||
Action = nameof(BackOfficeDefaultController.Index)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Creates routes for the preview hub
|
||||
@@ -37,8 +36,6 @@ public sealed class PreviewRoutes : IAreaRoutes
|
||||
case RuntimeLevel.Upgrade:
|
||||
case RuntimeLevel.Run:
|
||||
endpoints.MapHub<PreviewHub>(GetPreviewHubRoute());
|
||||
endpoints.MapUmbracoRoute<PreviewController>(_umbracoPathSegment, Constants.Web.Mvc.BackOfficeArea,
|
||||
null);
|
||||
break;
|
||||
case RuntimeLevel.BootFailed:
|
||||
case RuntimeLevel.Unknown:
|
||||
@@ -1,6 +1,6 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Security;
|
||||
namespace Umbraco.Cms.Api.Management.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Result returned from signing in when auto-linking takes place
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Custom <see cref="AuthenticationBuilder" /> used to associate external logins with umbraco external login options
|
||||
@@ -1,6 +1,6 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Security;
|
||||
namespace Umbraco.Cms.Api.Management.Security;
|
||||
|
||||
public class BackOfficeExternaLoginProviderScheme
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Security;
|
||||
namespace Umbraco.Cms.Api.Management.Security;
|
||||
|
||||
/// <summary>
|
||||
/// An external login (OAuth) provider for the back office
|
||||
@@ -1,6 +1,6 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Security;
|
||||
namespace Umbraco.Cms.Api.Management.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Options used to configure back office external login providers
|
||||
@@ -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;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class BackOfficeExternalLoginProviders : IBackOfficeExternalLoginProviders, ILocalLoginSettingProvider
|
||||
@@ -1,6 +1,6 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Security;
|
||||
namespace Umbraco.Cms.Api.Management.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Used to add back office login providers
|
||||
@@ -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
|
||||
{
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Custom secure format that ensures the Identity in the ticket is verified <see cref="ClaimsIdentity" />
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// The sign in manager for back office users
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Used to configure <see cref="BackOfficeIdentityOptions" /> for the Umbraco Back office
|
||||
@@ -17,12 +17,6 @@ public sealed class ConfigureBackOfficeIdentityOptions : IConfigureOptions<BackO
|
||||
private readonly UserPasswordConfigurationSettings _userPasswordConfiguration;
|
||||
private readonly SecuritySettings _securitySettings;
|
||||
|
||||
[Obsolete("Use the constructor that accepts SecuritySettings. Will be removed in V13.")]
|
||||
public ConfigureBackOfficeIdentityOptions(IOptions<UserPasswordConfigurationSettings> userPasswordConfiguration)
|
||||
: this(userPasswordConfiguration, StaticServiceProvider.Instance.GetRequiredService<IOptions<SecuritySettings>>())
|
||||
{
|
||||
}
|
||||
|
||||
public ConfigureBackOfficeIdentityOptions(
|
||||
IOptions<UserPasswordConfigurationSettings> userPasswordConfiguration,
|
||||
IOptions<SecuritySettings> securitySettings)
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Umbraco.Cms.Web.BackOffice.Security;
|
||||
namespace Umbraco.Cms.Api.Management.Security;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class DefaultBackOfficeTwoFactorOptions : IBackOfficeTwoFactorOptions
|
||||
@@ -1,6 +1,6 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Security;
|
||||
namespace Umbraco.Cms.Api.Management.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Result returned from signing in when external logins are used.
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Options used to configure auto-linking external OAuth providers
|
||||
@@ -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<BackOfficeController>(),
|
||||
new { area = Constants.Web.Mvc.BackOfficeArea, invite = forgotPasswordToken });
|
||||
// string? action = _linkGenerator.GetPathByAction(
|
||||
// nameof(BackOfficeController.ValidatePasswordResetCode),
|
||||
// ControllerExtensions.GetControllerName<BackOfficeController>(),
|
||||
// new { area = Constants.Web.Mvc.BackOfficeArea, invite = forgotPasswordToken });
|
||||
string action = string.Empty;
|
||||
|
||||
Uri applicationUri = _httpContextAccessor
|
||||
.GetRequiredHttpContext()
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Umbraco.Cms.Web.BackOffice.Security;
|
||||
namespace Umbraco.Cms.Api.Management.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Service to return <see cref="BackOfficeExternalLoginProvider" /> instances
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Umbraco.Cms.Web.BackOffice.Security;
|
||||
namespace Umbraco.Cms.Api.Management.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Options used to control 2FA for the Umbraco back office.
|
||||
@@ -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<BackOfficeController>(),
|
||||
new { area = Constants.Web.Mvc.BackOfficeArea, invite = inviteToken });
|
||||
// string? action = _linkGenerator.GetPathByAction(
|
||||
// nameof(BackOfficeController.VerifyInvite),
|
||||
// ControllerExtensions.GetControllerName<BackOfficeController>(),
|
||||
// new { area = Constants.Web.Mvc.BackOfficeArea, invite = inviteToken });
|
||||
string action = string.Empty;
|
||||
|
||||
Uri applicationUri = _httpContextAccessor
|
||||
.GetRequiredHttpContext()
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Umbraco.Cms.Web.BackOffice.Security;
|
||||
namespace Umbraco.Cms.Api.Management.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Options used as named options for 2fa providers
|
||||
@@ -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
|
||||
/// <inheritdoc />
|
||||
public bool HasConflictingRoutes(out string controllerName)
|
||||
{
|
||||
var controllers = _typeLoader.GetTypes<UmbracoApiControllerBase>().ToList();
|
||||
var controllers = _typeLoader.GetTypes<ManagementApiControllerBase>().ToList();
|
||||
foreach (Type controller in controllers)
|
||||
{
|
||||
Type[] potentialConflicting = controllers.Where(x => x.Name == controller.Name).ToArray();
|
||||
@@ -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)
|
||||
{
|
||||
@@ -9,23 +9,16 @@
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<ProjectReference Include="..\Umbraco.Cms.Api.Management\Umbraco.Cms.Api.Management.csproj" />
|
||||
<ProjectReference Include="..\Umbraco.Web.BackOffice\Umbraco.Web.BackOffice.csproj" />
|
||||
<ProjectReference Include="..\Umbraco.Web.Website\Umbraco.Web.Website.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<BasePath>$(ProjectDir)wwwroot\umbraco</BasePath>
|
||||
<BellePath>$(BasePath)\lib</BellePath>
|
||||
<BellissimaPath>$(BasePath)\backoffice</BellissimaPath>
|
||||
<LoginPath>$(BasePath)\login</LoginPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="BuildStaticAssetsPreconditions" BeforeTargets="Build">
|
||||
<Message Text="Skip BuildBelle target because UmbracoBuild is '$(UmbracoBuild)' (this is not Visual Studio)" Importance="high" Condition="'$(UmbracoBuild)' != ''" />
|
||||
<Message Text="Skip BuildBelle target because '$(BellePath)' already exists" Importance="high" Condition="Exists('$(BellePath)')" />
|
||||
<Message Text="Call BuildBelle target because UmbracoBuild is empty (this is Visual Studio) and '$(BellePath)' doesn't exist" Importance="high" Condition="'$(UmbracoBuild)' == '' and !Exists('$(BellePath)')" />
|
||||
<CallTarget Targets="BuildBelle" Condition="'$(UmbracoBuild)' == '' and !Exists('$(BellePath)')" />
|
||||
|
||||
<Message Text="Skip BuildBellissima target because UmbracoBuild is '$(UmbracoBuild)' (this is not Visual Studio)" Importance="high" Condition="'$(UmbracoBuild)' != ''" />
|
||||
<Message Text="Skip BuildBellissima target because '$(BellissimaPath)' already exists" Importance="high" Condition="Exists('$(BellissimaPath)')" />
|
||||
<Message Text="Call BuildBellissima target because UmbracoBuild is empty (this is Visual Studio) and '$(BellissimaPath)' doesn't exist" Importance="high" Condition="'$(UmbracoBuild)' == '' and !Exists('$(BellissimaPath)')" />
|
||||
@@ -37,11 +30,6 @@
|
||||
<CallTarget Targets="BuildLogin" Condition="'$(UmbracoBuild)' == '' and (!Exists('$(LoginPath)') or !Exists('$(BasePath)/auth'))" />
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildBelle">
|
||||
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Client\" Command="npm ci --no-fund --no-audit --prefer-offline" />
|
||||
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.Client\" Command="npm run build:skip-tests" />
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildBellissima">
|
||||
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.New.Client\" Command="npm ci --no-fund --no-audit --prefer-offline" />
|
||||
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.New.Client\" Command="npm run build:for:cms" />
|
||||
@@ -54,10 +42,6 @@
|
||||
</Target>
|
||||
|
||||
<Target Name="CleanStaticAssetsPreconditions" AfterTargets="Clean" Condition="'$(UmbracoBuild)' == ''">
|
||||
<Message Text="Skip CleanBelle target because '$(BellePath)' doesn't exist" Importance="high" Condition="!Exists('$(BellePath)')" />
|
||||
<Message Text="Skip CleanBelle target because preserve.belle marker file exists" Importance="high" Condition="Exists('$(BellePath)') and Exists('$(SolutionDir)preserve.belle')" />
|
||||
<Message Text="Call CleanBelle target because '$(BellePath)' exists and preserve.belle marker file doesn't exist" Importance="high" Condition="Exists('$(BellePath)') and !Exists('$(SolutionDir)preserve.belle')" />
|
||||
<CallTarget Targets="CleanBelle" Condition="Exists('$(BellePath)') and !Exists('$(SolutionDir)preserve.belle')" />
|
||||
|
||||
<Message Text="Skip CleanBellissima target because '$(BellissimaPath)' doesn't exist" Importance="high" Condition="!Exists('$(BellissimaPath)')" />
|
||||
<Message Text="Skip CleanBellissima target because preserve.bellissima marker file exists" Importance="high" Condition="Exists('$(BellissimaPath)') and Exists('$(SolutionDir)preserve.bellissima')" />
|
||||
@@ -70,13 +54,6 @@
|
||||
<CallTarget Targets="CleanLogin" Condition="(Exists('$(LoginPath)') or Exists('$(BasePath)/auth')) and !Exists('$(SolutionDir)preserve.login')" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CleanBelle">
|
||||
<ItemGroup>
|
||||
<BelleDirectories Include="$(BasePath)/js;$(BasePath)/lib;$(BasePath)/assets;$(BasePath)/views;" />
|
||||
</ItemGroup>
|
||||
<RemoveDir Directories="@(BelleDirectories)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CleanBellissima">
|
||||
<ItemGroup>
|
||||
<BellissimaDirectories Include="$(BellissimaPath);" />
|
||||
|
||||
@@ -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> globalSettings
|
||||
@inject IBackOfficeExternalLoginProviders externalLogins
|
||||
@inject IRuntimeMinifier runtimeMinifier
|
||||
|
||||
@{
|
||||
var backOfficePath = globalSettings.Value.GetBackOfficePath(hostingEnvironment);
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<base href="@backOfficePath.EnsureEndsWith('/')" />
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Umbraco</title>
|
||||
|
||||
@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*@
|
||||
<style>
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body id="umbracoMainPageBody" ng-controller="Umbraco.AuthorizeUpgradeController" class="login-only">
|
||||
|
||||
<umb-login on-login="loginAndRedirect()"></umb-login>
|
||||
|
||||
<umb-notifications></umb-notifications>
|
||||
|
||||
@{
|
||||
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)
|
||||
|
||||
<script type="text/javascript">
|
||||
document.angularReady = function (app) {
|
||||
|
||||
@await Html.AngularValueExternalLoginInfoScriptAsync(externalLogins, ViewData.GetExternalSignInProviderErrors()!)
|
||||
@Html.AngularValueResetPasswordCodeInfoScript(ViewData[ViewDataExtensions.TokenPasswordResetCode]!)
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
@*And finally we can load in our angular app*@
|
||||
<script type="text/javascript" src="lib/lazyload-js/LazyLoad.min.js"></script>
|
||||
<script src="@Url.GetUrlWithCacheBust("Application", "BackOffice", null!, hostingEnvironment, umbracoVersion, runtimeMinifier)"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="@CultureInfo.CurrentCulture.Name">
|
||||
|
||||
<head>
|
||||
<base href="@backOfficePath.EnsureEndsWith('/')" />
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="@backOfficeAssetsPath/assets/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Umbraco</title>
|
||||
|
||||
<link rel="stylesheet" href="@backOfficeAssetsPath/css/umb-css.css" />
|
||||
<link rel="stylesheet" href="@backOfficeAssetsPath/css/uui-css.css" />
|
||||
@await Html.BackOfficeImportMapScriptAsync(JsonSerializer, BackOfficePathGenerator, PackageManifestService)
|
||||
<script type="module" src="@backOfficeAssetsPath/apps/app/app.element.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="uui-font uui-text" style="margin: 0; padding: 0; overflow: hidden">
|
||||
<umb-app culture="@CultureInfo.CurrentCulture.Name"></umb-app>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -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
|
||||
@@ -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> 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");
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Umbraco Preview</title>
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta name="pinterest" content="nopin" />
|
||||
|
||||
@Html.Raw(await RuntimeMinifier.RenderCssHereAsync(BackOfficeWebAssets.UmbracoPreviewCssBundleName))
|
||||
|
||||
<script type="text/javascript">
|
||||
window.umbLocalizedVars = {
|
||||
'returnToPreviewHeadline': '@returnToPreviewHeadline',
|
||||
'returnToPreviewDescription':'@returnToPreviewDescription',
|
||||
'returnToPreviewAcceptButton':'@returnToPreviewAcceptButton',
|
||||
'returnToPreviewDeclineButton':'@returnToPreviewDeclineButton'
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body id="canvasdesignerPanel" ng-mouseover="outlinePositionHide()" ng-controller="previewController" ng-class="{'tabbing-active': tabbingActive === true}" ng-click="windowClickHandler($event)">
|
||||
<div class="wait" ng-show="!frameLoaded"></div>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(Model?.PreviewExtendedHeaderView))
|
||||
{
|
||||
@await Html.PartialAsync(Model.PreviewExtendedHeaderView)
|
||||
}
|
||||
|
||||
<div id="demo-iframe-wrapper" class="{{previewDevice.css}}">
|
||||
<preview-i-frame src="pageUrl" on-loaded="onFrameLoaded(iframe)"></preview-i-frame>
|
||||
</div>
|
||||
<div class="canvasdesigner" ng-init="showDevicesPreview = true; showDevices = !@(disableDevicePreview);" ng-mouseenter="positionSelectedHide()">
|
||||
<div class="menu-bar selected">
|
||||
|
||||
<div class="menu-bar__title">Preview Mode</div>
|
||||
|
||||
<div class="menu-bar__right-part">
|
||||
|
||||
<div class="preview-menu-option" ng-class="{'--open': sizeOpen === true}" ng-click="$event.stopPropagation()">
|
||||
<button class="menu-bar__button umb-outline" ng-click="toggleSizeOpen()"><i class="icon {{previewDevice.icon}}"></i><span>{{previewDevice.title}}</span></button>
|
||||
<div class="dropdown-menu">
|
||||
<button ng-repeat="device in devices" class="menu-bar__button umb-outline" ng-class="{ '--active':previewDevice === device }" ng-click="updatePreviewDevice(device)">
|
||||
<i class="icon {{device.icon}}"></i><span>{{device.title}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model?.Languages.Any() == true)
|
||||
{
|
||||
<div class="preview-menu-option" ng-class="{'--open': cultureOpen === true}" ng-click="$event.stopPropagation()">
|
||||
<button class="menu-bar__button umb-outline" ng-click="toggleCultureOpen()"><i class="icon icon-globe-europe---africa"></i><span>{{currentCulture.title}}</span></button>
|
||||
<div class="dropdown-menu">
|
||||
@foreach (var language in Model.Languages)
|
||||
{
|
||||
<button class="menu-bar__button umb-outline" ng-class="{ '--active': currentCultureIso === '@language.IsoCode' || (@language.IsDefault.ToString().ToLower() && currentCultureIso === null) }" ng-click="changeCulture('@language.IsoCode')" ng-init="registerCulture('@language.IsoCode', '@language.CultureName', @language.IsDefault.ToString().ToLower())">
|
||||
<i class="icon icon-globe-europe---africa"></i><span>@language.CultureName</span>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<button ng-click="openInBrowser()" title="@OpenWebsiteTitle" class="menu-bar__button umb-outline">
|
||||
<i class="icon icon-out"></i><span>@OpenWebsiteLabel</span>
|
||||
</button>
|
||||
|
||||
<button ng-click="exitPreview()" title="@EndTitle" class="menu-bar__button umb-outline">
|
||||
<i class="icon icon-power"></i><span>@EndLabel</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@await Html.BareMinimumServerVariablesScriptAsync(BackOfficeServerVariables)
|
||||
|
||||
<script src="../lib/lazyload-js/LazyLoad.min.js"></script>
|
||||
<script src="@Url.GetUrlWithCacheBust("Application", "Preview", null!, HostingEnvironment, UmbracoVersion, RuntimeMinifier)"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -97,7 +97,6 @@ public static partial class UmbracoBuilderExtensions
|
||||
.Append<Hulu>()
|
||||
.Append<Giphy>()
|
||||
.Append<LottieFiles>();
|
||||
builder.SearchableTrees().Add(() => builder.TypeLoader.GetTypes<ISearchableTree>());
|
||||
builder.BackOfficeAssets();
|
||||
builder.SelectorHandlers().Add(() => builder.TypeLoader.GetTypes<ISelectorHandler>());
|
||||
builder.FilterHandlers().Add(() => builder.TypeLoader.GetTypes<IFilterHandler>());
|
||||
@@ -245,12 +244,6 @@ public static partial class UmbracoBuilderExtensions
|
||||
public static EmbedProvidersCollectionBuilder EmbedProviders(this IUmbracoBuilder builder)
|
||||
=> builder.WithCollectionBuilder<EmbedProvidersCollectionBuilder>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the back office searchable tree collection builder
|
||||
/// </summary>
|
||||
public static SearchableTreeCollectionBuilder SearchableTrees(this IUmbracoBuilder builder)
|
||||
=> builder.WithCollectionBuilder<SearchableTreeCollectionBuilder>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the back office custom assets collection builder
|
||||
/// </summary>
|
||||
|
||||
@@ -217,7 +217,7 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
|
||||
Services.AddUnique<IEventMessagesFactory, DefaultEventMessagesFactory>();
|
||||
Services.AddUnique<IEventMessagesAccessor, HybridEventMessagesAccessor>();
|
||||
Services.AddUnique<ITreeService, TreeService>();
|
||||
|
||||
Services.AddUnique<ISmsSender, NotImplementedSmsSender>();
|
||||
Services.AddUnique<IEmailSender, NotImplementedEmailSender>();
|
||||
|
||||
@@ -369,7 +369,7 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
Services.AddUnique<IWebhookLogService, WebhookLogService>();
|
||||
Services.AddUnique<IWebhookLogFactory, WebhookLogFactory>();
|
||||
Services.AddUnique<IWebhookRequestService, WebhookRequestService>();
|
||||
|
||||
|
||||
// Data type configuration cache
|
||||
Services.AddUnique<IDataTypeConfigurationCache, DataTypeConfigurationCache>();
|
||||
Services.AddNotificationHandler<DataTypeCacheRefresherNotification, DataTypeConfigurationCacheRefresher>();
|
||||
|
||||
@@ -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<ContentPropertyBasic> _contentPropertyBasicConverter;
|
||||
private readonly ContentPropertyDisplayMapper _contentPropertyDisplayMapper;
|
||||
private readonly ContentPropertyDtoMapper _contentPropertyDtoConverter;
|
||||
private readonly CommonMapper _commonMapper;
|
||||
private readonly ContentBasicSavedStateMapper<ContentPropertyBasic> _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<ContentPropertyBasic>();
|
||||
_contentPropertyBasicConverter = new ContentPropertyBasicMapper<ContentPropertyBasic>(
|
||||
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<IDataTypeConfigurationCache>())
|
||||
{ }
|
||||
|
||||
public void DefineMaps(IUmbracoMapper mapper)
|
||||
{
|
||||
mapper.Define<PropertyGroup, Tab<ContentPropertyDisplay>>(
|
||||
@@ -74,6 +62,8 @@ public class ContentPropertyMapDefinition : IMapDefinition
|
||||
mapper.Define<IProperty, ContentPropertyBasic>((source, context) => new ContentPropertyBasic(), Map);
|
||||
mapper.Define<IProperty, ContentPropertyDto>((source, context) => new ContentPropertyDto(), Map);
|
||||
mapper.Define<IProperty, ContentPropertyDisplay>((source, context) => new ContentPropertyDisplay(), Map);
|
||||
mapper.Define<IContent, ContentItemBasic<ContentPropertyBasic>>((source, context) => new ContentItemBasic<ContentPropertyBasic>(), Map);
|
||||
mapper.Define<IContent, ContentPropertyCollectionDto>((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<ContentPropertyBasic> 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<IProperty, ContentPropertyBasic>(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<IProperty, ContentPropertyDto>(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;
|
||||
}
|
||||
}
|
||||
|
||||
50
src/Umbraco.Core/Models/Mapping/MediaMapDefinition.cs
Normal file
50
src/Umbraco.Core/Models/Mapping/MediaMapDefinition.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models.Mapping;
|
||||
|
||||
/// <summary>
|
||||
/// Declares model mappings for media.
|
||||
/// </summary>
|
||||
public class MediaMapDefinition : IMapDefinition
|
||||
{
|
||||
private readonly CommonMapper _commonMapper;
|
||||
|
||||
public MediaMapDefinition(CommonMapper commonMapper)
|
||||
{
|
||||
_commonMapper = commonMapper;
|
||||
}
|
||||
|
||||
public void DefineMaps(IUmbracoMapper mapper)
|
||||
{
|
||||
mapper.Define<IMedia, ContentPropertyCollectionDto>((source, context) => new ContentPropertyCollectionDto(), Map);
|
||||
mapper.Define<IMedia, ContentItemBasic<ContentPropertyBasic>>((source, context) => new ContentItemBasic<ContentPropertyBasic>(), Map);
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll
|
||||
private static void Map(IMedia source, ContentPropertyCollectionDto target, MapperContext context) =>
|
||||
target.Properties = context.MapEnumerable<IProperty, ContentPropertyDto>(source.Properties).WhereNotNull();
|
||||
|
||||
// Umbraco.Code.MapAll -Edited -Updater -Alias
|
||||
private void Map(IMedia source, ContentItemBasic<ContentPropertyBasic> 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<IProperty, ContentPropertyBasic>(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();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,23 @@
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models.Mapping;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class MemberMapDefinition : IMapDefinition
|
||||
{
|
||||
private readonly CommonMapper _commonMapper;
|
||||
|
||||
public MemberMapDefinition(CommonMapper commonMapper) => _commonMapper = commonMapper;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void DefineMaps(IUmbracoMapper mapper) => mapper.Define<MemberSave, IMember>(Map);
|
||||
public void DefineMaps(IUmbracoMapper mapper)
|
||||
{
|
||||
mapper.Define<MemberSave, IMember>(Map);
|
||||
mapper.Define<IMember, MemberBasic>((source, context) => new MemberBasic(), Map);
|
||||
mapper.Define<IMember, ContentPropertyCollectionDto>((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<IProperty, ContentPropertyBasic>(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<IProperty, ContentPropertyDto>(source.Properties).WhereNotNull();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services;
|
||||
|
||||
public interface IIconService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the svg string for the icon name found at the global icons path
|
||||
/// </summary>
|
||||
/// <param name="iconName"></param>
|
||||
/// <returns></returns>
|
||||
IconModel? GetIcon(string iconName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all svg icons found at at the global icons path.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IReadOnlyDictionary<string, string>? GetIcons();
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using Umbraco.Cms.Core.Trees;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a service which manages section trees.
|
||||
/// </summary>
|
||||
public interface ITreeService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a tree.
|
||||
/// </summary>
|
||||
/// <param name="treeAlias">The tree alias.</param>
|
||||
Tree? GetByAlias(string treeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all trees.
|
||||
/// </summary>
|
||||
IEnumerable<Tree> GetAll(TreeUse use = TreeUse.Main);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all trees for a section.
|
||||
/// </summary>
|
||||
IEnumerable<Tree> GetBySection(string sectionAlias, TreeUse use = TreeUse.Main);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all trees for a section, grouped.
|
||||
/// </summary>
|
||||
IDictionary<string, IEnumerable<Tree>> GetBySectionGrouped(string sectionAlias, TreeUse use = TreeUse.Main);
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using Umbraco.Cms.Core.Trees;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Implements <see cref="ITreeService" />.
|
||||
/// </summary>
|
||||
public class TreeService : ITreeService
|
||||
{
|
||||
private readonly TreeCollection _treeCollection;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TreeService" /> class.
|
||||
/// </summary>
|
||||
/// <param name="treeCollection"></param>
|
||||
public TreeService(TreeCollection treeCollection) => _treeCollection = treeCollection;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Tree? GetByAlias(string treeAlias) => _treeCollection.FirstOrDefault(x => x.TreeAlias == treeAlias);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Tree> 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));
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Tree> 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();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDictionary<string, IEnumerable<Tree>>
|
||||
GetBySectionGrouped(string sectionAlias, TreeUse use = TreeUse.Main) =>
|
||||
GetBySection(sectionAlias, use).GroupBy(x => x.TreeGroup).ToDictionary(
|
||||
x => x.Key ?? string.Empty,
|
||||
x => (IEnumerable<Tree>)x.ToArray());
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
namespace Umbraco.Cms.Core.Trees;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class SearchableTreeAttribute : Attribute
|
||||
{
|
||||
public const int DefaultSortOrder = 1000;
|
||||
|
||||
/// <summary>
|
||||
/// This constructor will assume that the method name equals `format(searchResult, appAlias, treeAlias)`.
|
||||
/// </summary>
|
||||
/// <param name="serviceName">Name of the service.</param>
|
||||
public SearchableTreeAttribute(string serviceName)
|
||||
: this(serviceName, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This constructor defines both the Angular service and method name to use.
|
||||
/// </summary>
|
||||
/// <param name="serviceName">Name of the service.</param>
|
||||
/// <param name="methodName">Name of the method.</param>
|
||||
public SearchableTreeAttribute(string serviceName, string methodName)
|
||||
: this(serviceName, methodName, DefaultSortOrder)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This constructor defines both the Angular service and method name to use and explicitly defines a sort order for
|
||||
/// the results
|
||||
/// </summary>
|
||||
/// <param name="serviceName">Name of the service.</param>
|
||||
/// <param name="methodName">Name of the method.</param>
|
||||
/// <param name="sortOrder">The sort order.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// serviceName
|
||||
/// or
|
||||
/// methodName
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">Value can't be empty or consist only of white-space characters. - serviceName</exception>
|
||||
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; }
|
||||
}
|
||||
@@ -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<ISearchableTree>
|
||||
{
|
||||
private readonly Dictionary<string, SearchableApplicationTree> _dictionary;
|
||||
|
||||
public SearchableTreeCollection(Func<IEnumerable<ISearchableTree>> items, ITreeService treeService)
|
||||
: base(items) =>
|
||||
_dictionary = CreateDictionary(treeService);
|
||||
|
||||
public IReadOnlyDictionary<string, SearchableApplicationTree> SearchableApplicationTrees => _dictionary;
|
||||
|
||||
public SearchableApplicationTree this[string key] => _dictionary[key];
|
||||
|
||||
private Dictionary<string, SearchableApplicationTree> CreateDictionary(ITreeService treeService)
|
||||
{
|
||||
Tree[] appTrees = treeService.GetAll()
|
||||
.OrderBy(x => x.SortOrder)
|
||||
.ToArray();
|
||||
var dictionary = new Dictionary<string, SearchableApplicationTree>(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<SearchableTreeAttribute>(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;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
|
||||
namespace Umbraco.Cms.Core.Trees;
|
||||
|
||||
public class SearchableTreeCollectionBuilder : LazyCollectionBuilderBase<SearchableTreeCollectionBuilder,
|
||||
SearchableTreeCollection, ISearchableTree>
|
||||
{
|
||||
protected override SearchableTreeCollectionBuilder This => this;
|
||||
|
||||
// per request because generally an instance of ISearchableTree is a controller
|
||||
protected override ServiceLifetime CollectionLifetime => ServiceLifetime.Scoped;
|
||||
}
|
||||
@@ -255,21 +255,6 @@ public static partial class UmbracoBuilderExtensions
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IUmbracoBuilder AddLogViewer(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddSingleton<ILogViewerConfig, LogViewerConfig>();
|
||||
builder.Services.AddSingleton<ILogLevelLoader, LogLevelLoader>();
|
||||
builder.SetLogViewer<SerilogJsonLogViewer>();
|
||||
builder.Services.AddSingleton<ILogViewer>(factory => new SerilogJsonLogViewer(
|
||||
factory.GetRequiredService<ILogger<SerilogJsonLogViewer>>(),
|
||||
factory.GetRequiredService<ILogViewerConfig>(),
|
||||
factory.GetRequiredService<ILoggingConfiguration>(),
|
||||
factory.GetRequiredService<ILogLevelLoader>(),
|
||||
Log.Logger));
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds logging requirements for Umbraco
|
||||
/// </summary>
|
||||
|
||||
@@ -31,6 +31,7 @@ public static partial class UmbracoBuilderExtensions
|
||||
.Add<TagMapDefinition>()
|
||||
.Add<TemplateMapDefinition>()
|
||||
.Add<UserMapDefinition>()
|
||||
.Add<MediaMapDefinition>()
|
||||
.Add<MemberMapDefinition>()
|
||||
.Add<LanguageMapDefinition>()
|
||||
.Add<IdentityMapDefinition>();
|
||||
|
||||
@@ -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<RichTextBlockValue, RichTextBlockLayoutItem>? blockEditorData = ConvertAndClean(richTextEditorValue.Blocks);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 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 {
|
||||
|
||||
/// <summary>
|
||||
/// 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]";.
|
||||
/// </summary>
|
||||
internal static string JsInitialize {
|
||||
get {
|
||||
@@ -130,35 +129,20 @@ namespace Umbraco.Cms.Infrastructure.WebAssets {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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## ;.
|
||||
/// </summary>
|
||||
internal static string ServerVariables {
|
||||
get {
|
||||
return ResourceManager.GetString("ServerVariables", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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'
|
||||
///]
|
||||
///.
|
||||
/// </summary>
|
||||
internal static string TinyMceInitialize {
|
||||
get {
|
||||
|
||||
@@ -28,9 +28,6 @@
|
||||
<data name="PreviewInitialize" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>PreviewInitialize.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
|
||||
</data>
|
||||
<data name="ServerVariables" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>ServerVariables.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
|
||||
</data>
|
||||
<data name="TinyMceInitialize" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>TinyMceInitialize.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
|
||||
</data>
|
||||
|
||||
@@ -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## ;
|
||||
@@ -1,33 +0,0 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.WebAssets;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the server variables are included in the outgoing JS script
|
||||
/// </summary>
|
||||
public class ServerVariablesParser
|
||||
{
|
||||
private const string Token = "##Variables##";
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServerVariablesParser" /> class.
|
||||
/// </summary>
|
||||
public ServerVariablesParser(IEventAggregator eventAggregator) => _eventAggregator = eventAggregator;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the server variables in the dictionary are included in the outgoing JS script
|
||||
/// </summary>
|
||||
public async Task<string> ParseAsync(Dictionary<string, object> 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());
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// If the users being edited is an admin then we must ensure that the current user is also an admin.
|
||||
/// </summary>
|
||||
public class AdminUsersHandler : MustSatisfyRequirementAuthorizationHandler<AdminUsersRequirement>
|
||||
{
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly UserEditorAuthorizationHelper _userEditorAuthorizationHelper;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AdminUsersHandler" /> class.
|
||||
/// </summary>
|
||||
/// <param name="httpContextAccessor">Accessor for the HTTP context of the current request.</param>
|
||||
/// <param name="userService">Service for user related operations.</param>
|
||||
/// <param name="backOfficeSecurityAccessor">Accessor for back-office security.</param>
|
||||
/// <param name="userEditorAuthorizationHelper">Helper for user authorization checks.</param>
|
||||
public AdminUsersHandler(
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IUserService userService,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
UserEditorAuthorizationHelper userEditorAuthorizationHelper)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_userService = userService;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
_userEditorAuthorizationHelper = userEditorAuthorizationHelper;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<bool> 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<int>.Succeed(output)
|
||||
: Attempt<int>.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<IUser> users = _userService.GetUsersById(userIds);
|
||||
var isAuth = users.All(user =>
|
||||
_userEditorAuthorizationHelper.IsAuthorized(_backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser,
|
||||
user, null, null, null) != false);
|
||||
|
||||
return Task.FromResult(isAuth);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// Authorization requirement for the <see cref="AdminUsersHandler" />
|
||||
/// </summary>
|
||||
public class AdminUsersRequirement : IAuthorizationRequirement
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AdminUsersRequirement" /> class.
|
||||
/// </summary>
|
||||
/// <param name="queryStringName">Query string name from which to authorize values.</param>
|
||||
public AdminUsersRequirement(string queryStringName = "id") => QueryStringName = queryStringName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the query string name from which to authorize values.
|
||||
/// </summary>
|
||||
public string QueryStringName { get; }
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures authorization is successful for a back office user.
|
||||
/// </summary>
|
||||
public class BackOfficeHandler : MustSatisfyRequirementAuthorizationHandler<BackOfficeRequirement>
|
||||
{
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurity;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
|
||||
public BackOfficeHandler(IBackOfficeSecurityAccessor backOfficeSecurity, IRuntimeState runtimeState)
|
||||
{
|
||||
_backOfficeSecurity = backOfficeSecurity;
|
||||
_runtimeState = runtimeState;
|
||||
}
|
||||
|
||||
protected override Task<bool> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// Authorization requirement for the <see cref="BackOfficeHandler" />.
|
||||
/// </summary>
|
||||
public class BackOfficeRequirement : IAuthorizationRequirement
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BackOfficeRequirement" /> class.
|
||||
/// </summary>
|
||||
/// <param name="requireApproval">Flag for whether back-office user approval is required.</param>
|
||||
public BackOfficeRequirement(bool requireApproval = true) => RequireApproval = requireApproval;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether back-office user approval is required.
|
||||
/// </summary>
|
||||
public bool RequireApproval { get; }
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// The user must have access to all descendant nodes of the content item in order to continue.
|
||||
/// </summary>
|
||||
public class ContentPermissionsPublishBranchHandler : MustSatisfyRequirementAuthorizationHandler<
|
||||
ContentPermissionsPublishBranchRequirement, IContent>
|
||||
{
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
private readonly ContentPermissions _contentPermissions;
|
||||
private readonly IEntityService _entityService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentPermissionsPublishBranchHandler" /> class.
|
||||
/// </summary>
|
||||
/// <param name="entityService">Service for entity operations.</param>
|
||||
/// <param name="contentPermissions">per for user content authorization checks.</param>
|
||||
/// <param name="backOfficeSecurityAccessor">Accessor for back-office security.</param>
|
||||
public ContentPermissionsPublishBranchHandler(
|
||||
IEntityService entityService,
|
||||
ContentPermissions contentPermissions,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
{
|
||||
_entityService = entityService;
|
||||
_contentPermissions = contentPermissions;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<bool> IsAuthorized(AuthorizationHandlerContext context,
|
||||
ContentPermissionsPublishBranchRequirement requirement, IContent resource)
|
||||
{
|
||||
IUser? currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser;
|
||||
|
||||
var denied = new List<IUmbracoEntity>();
|
||||
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<IEntitySlim> 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);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// Authorization requirement for <see cref="ContentPermissionsPublishBranchHandler" />
|
||||
/// </summary>
|
||||
public class ContentPermissionsPublishBranchRequirement : IAuthorizationRequirement
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentPermissionsPublishBranchRequirement" /> class.
|
||||
/// </summary>
|
||||
/// <param name="permission">Permission to check.</param>
|
||||
public ContentPermissionsPublishBranchRequirement(char permission) => Permission = permission;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value for the permission to check.
|
||||
/// </summary>
|
||||
public char Permission { get; }
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Used to authorize if the user has the correct permission access to the content for the content id specified in a
|
||||
/// query string.
|
||||
/// </summary>
|
||||
public class
|
||||
ContentPermissionsQueryStringHandler : PermissionsQueryStringHandler<ContentPermissionsQueryStringRequirement>
|
||||
{
|
||||
private readonly ContentPermissions _contentPermissions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentPermissionsQueryStringHandler" /> class.
|
||||
/// </summary>
|
||||
/// <param name="backOfficeSecurityAccessor">Accessor for back-office security.</param>
|
||||
/// <param name="httpContextAccessor">Accessor for the HTTP context of the current request.</param>
|
||||
/// <param name="entityService">Service for entity operations.</param>
|
||||
/// <param name="contentPermissions">Helper for content authorization checks.</param>
|
||||
public ContentPermissionsQueryStringHandler(
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IEntityService entityService,
|
||||
ContentPermissions contentPermissions)
|
||||
: base(backOfficeSecurityAccessor, httpContextAccessor, entityService) =>
|
||||
_contentPermissions = contentPermissions;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<bool> 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)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// An authorization requirement for <see cref="ContentPermissionsQueryStringHandler" />
|
||||
/// </summary>
|
||||
public class ContentPermissionsQueryStringRequirement : IAuthorizationRequirement
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentPermissionsQueryStringRequirement" /> class for a specific node
|
||||
/// id.
|
||||
/// </summary>
|
||||
/// <param name="nodeId">The node Id.</param>
|
||||
/// <param name="permissionToCheck">The permission to authorize the current user against.</param>
|
||||
public ContentPermissionsQueryStringRequirement(int nodeId, char permissionToCheck)
|
||||
{
|
||||
NodeId = nodeId;
|
||||
PermissionToCheck = permissionToCheck;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentPermissionsQueryStringRequirement" /> class for a
|
||||
/// node id based on a query string parameter.
|
||||
/// </summary>
|
||||
/// <param name="paramName">The querystring parameter name.</param>
|
||||
/// <param name="permissionToCheck">The permission to authorize the current user against.</param>
|
||||
public ContentPermissionsQueryStringRequirement(char permissionToCheck, string paramName = "id")
|
||||
{
|
||||
QueryStringName = paramName;
|
||||
PermissionToCheck = permissionToCheck;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specific node Id.
|
||||
/// </summary>
|
||||
public int? NodeId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the querystring parameter name.
|
||||
/// </summary>
|
||||
public string? QueryStringName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the permission to authorize the current user against.
|
||||
/// </summary>
|
||||
public char PermissionToCheck { get; }
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// The resource used for the <see cref="ContentPermissionsResourceRequirement" />
|
||||
/// </summary>
|
||||
public class ContentPermissionsResource
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentPermissionsResource" /> class.
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <param name="permissionToCheck">The permission to authorize.</param>
|
||||
public ContentPermissionsResource(IContent? content, char permissionToCheck)
|
||||
{
|
||||
PermissionsToCheck = new List<char> { permissionToCheck };
|
||||
Content = content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentPermissionsResource" /> class.
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <param name="permissionsToCheck">The collection of permissions to authorize.</param>
|
||||
public ContentPermissionsResource(IContent content, IReadOnlyList<char> permissionsToCheck)
|
||||
{
|
||||
Content = content;
|
||||
PermissionsToCheck = permissionsToCheck;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentPermissionsResource" /> class.
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <param name="nodeId">The node Id.</param>
|
||||
/// <param name="permissionsToCheck">The collection of permissions to authorize.</param>
|
||||
public ContentPermissionsResource(IContent? content, int nodeId, IReadOnlyList<char> permissionsToCheck)
|
||||
{
|
||||
Content = content;
|
||||
NodeId = nodeId;
|
||||
PermissionsToCheck = permissionsToCheck;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the node Id.
|
||||
/// </summary>
|
||||
public int? NodeId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of permissions to authorize.
|
||||
/// </summary>
|
||||
public IReadOnlyList<char> PermissionsToCheck { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content.
|
||||
/// </summary>
|
||||
public IContent? Content { get; }
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Used to authorize if the user has the correct permission access to the content for the <see cref="IContent" />
|
||||
/// specified.
|
||||
/// </summary>
|
||||
public class ContentPermissionsResourceHandler : MustSatisfyRequirementAuthorizationHandler<
|
||||
ContentPermissionsResourceRequirement, ContentPermissionsResource>
|
||||
{
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
private readonly ContentPermissions _contentPermissions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentPermissionsResourceHandler" /> class.
|
||||
/// </summary>
|
||||
/// <param name="backOfficeSecurityAccessor">Accessor for back-office security.</param>
|
||||
/// <param name="contentPermissions">Helper for content authorization checks.</param>
|
||||
public ContentPermissionsResourceHandler(
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
ContentPermissions contentPermissions)
|
||||
{
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
_contentPermissions = contentPermissions;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<bool> 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);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// An authorization requirement for <see cref="ContentPermissionsResourceHandler" />
|
||||
/// </summary>
|
||||
public class ContentPermissionsResourceRequirement : IAuthorizationRequirement
|
||||
{
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the resource cannot be accessed if <see cref="IBackOfficeExternalLoginProviders.HasDenyLocalLogin" />
|
||||
/// returns true.
|
||||
/// </summary>
|
||||
public class DenyLocalLoginHandler : MustSatisfyRequirementAuthorizationHandler<DenyLocalLoginRequirement>
|
||||
{
|
||||
private readonly IBackOfficeExternalLoginProviders _externalLogins;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DenyLocalLoginHandler" /> class.
|
||||
/// </summary>
|
||||
/// <param name="externalLogins">Provides access to <see cref="BackOfficeExternalLoginProvider" /> instances.</param>
|
||||
public DenyLocalLoginHandler(IBackOfficeExternalLoginProviders externalLogins) => _externalLogins = externalLogins;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<bool> IsAuthorized(AuthorizationHandlerContext context,
|
||||
DenyLocalLoginRequirement requirement) =>
|
||||
Task.FromResult(!_externalLogins.HasDenyLocalLogin());
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// Marker requirement for the <see cref="DenyLocalLoginHandler" />.
|
||||
/// </summary>
|
||||
public class DenyLocalLoginRequirement : IAuthorizationRequirement
|
||||
{
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Used to authorize if the user has the correct permission access to the media for the media id specified in a query
|
||||
/// string.
|
||||
/// </summary>
|
||||
public class MediaPermissionsQueryStringHandler : PermissionsQueryStringHandler<MediaPermissionsQueryStringRequirement>
|
||||
{
|
||||
private readonly MediaPermissions _mediaPermissions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MediaPermissionsQueryStringHandler" /> class.
|
||||
/// </summary>
|
||||
/// <param name="backOfficeSecurityAccessor">Accessor for back-office security.</param>
|
||||
/// <param name="httpContextAccessor">Accessor for the HTTP context of the current request.</param>
|
||||
/// <param name="entityService">Service for entity operations.</param>
|
||||
/// <param name="mediaPermissions">Helper for media authorization checks.</param>
|
||||
public MediaPermissionsQueryStringHandler(
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IEntityService entityService,
|
||||
MediaPermissions mediaPermissions)
|
||||
: base(backOfficeSecurityAccessor, httpContextAccessor, entityService) => _mediaPermissions = mediaPermissions;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<bool> 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)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// An authorization requirement for <see cref="MediaPermissionsQueryStringHandler" />
|
||||
/// </summary>
|
||||
public class MediaPermissionsQueryStringRequirement : IAuthorizationRequirement
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MediaPermissionsQueryStringRequirement" /> class.
|
||||
/// </summary>
|
||||
/// <param name="paramName">Querystring paramter name.</param>
|
||||
public MediaPermissionsQueryStringRequirement(string paramName) => QueryStringName = paramName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the querystring paramter name.
|
||||
/// </summary>
|
||||
public string QueryStringName { get; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Used to authorize if the user has the correct permission access to the content for the <see cref="IContent" />
|
||||
/// specified.
|
||||
/// </summary>
|
||||
public class MediaPermissionsResourceHandler : MustSatisfyRequirementAuthorizationHandler<
|
||||
MediaPermissionsResourceRequirement, MediaPermissionsResource>
|
||||
{
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
private readonly MediaPermissions _mediaPermissions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MediaPermissionsResourceHandler" /> class.
|
||||
/// </summary>
|
||||
/// <param name="backOfficeSecurityAccessor">Accessor for back-office security.</param>
|
||||
/// <param name="mediaPermissions">Helper for media authorization checks.</param>
|
||||
public MediaPermissionsResourceHandler(
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
MediaPermissions mediaPermissions)
|
||||
{
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
_mediaPermissions = mediaPermissions;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<bool> 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);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// An authorization requirement for <see cref="MediaPermissionsResourceHandler" />
|
||||
/// </summary>
|
||||
public class MediaPermissionsResourceRequirement : IAuthorizationRequirement
|
||||
{
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// Abstract handler that must satisfy the requirement so Succeed or Fail will be called no matter what.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Authorization requirement.</typeparam>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public abstract class MustSatisfyRequirementAuthorizationHandler<T> : AuthorizationHandler<T>
|
||||
where T : IAuthorizationRequirement
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, T requirement)
|
||||
{
|
||||
var isAuth = await IsAuthorized(context, requirement);
|
||||
if (isAuth)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Fail();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true if the requirement is succeeded or ignored, return false if the requirement is explicitly not met
|
||||
/// </summary>
|
||||
/// <param name="context">The authorization context.</param>
|
||||
/// <param name="requirement">The authorization requirement.</param>
|
||||
/// <returns>True if request is authorized, false if not.</returns>
|
||||
protected abstract Task<bool> IsAuthorized(AuthorizationHandlerContext context, T requirement);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Abstract handler that must satisfy the requirement so Succeed or Fail will be called no matter what.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Authorization requirement.</typeparam>
|
||||
/// <typeparam name="TResource">Resource to authorize access to.</typeparam>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public abstract class MustSatisfyRequirementAuthorizationHandler<T, TResource> : AuthorizationHandler<T, TResource>
|
||||
where T : IAuthorizationRequirement
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true if the requirement is succeeded or ignored, return false if the requirement is explicitly not met
|
||||
/// </summary>
|
||||
/// <param name="context">The authorization context.</param>
|
||||
/// <param name="requirement">The authorization requirement.</param>
|
||||
/// <param name="resource">The resource to authorize access to.</param>
|
||||
/// <returns>True if request is authorized, false if not.</returns>
|
||||
protected abstract Task<bool> IsAuthorized(AuthorizationHandlerContext context, T requirement, TResource resource);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Abstract base class providing common functionality for authorization checks based on querystrings.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Authorization requirement</typeparam>
|
||||
public abstract class PermissionsQueryStringHandler<T> : MustSatisfyRequirementAuthorizationHandler<T>
|
||||
where T : IAuthorizationRequirement
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PermissionsQueryStringHandler{T}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="backOfficeSecurityAccessor">Accessor for back-office security.</param>
|
||||
/// <param name="httpContextAccessor">Accessor for the HTTP context of the current request.</param>
|
||||
/// <param name="entityService">Service for entity operations.</param>
|
||||
public PermissionsQueryStringHandler(
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IEntityService entityService)
|
||||
{
|
||||
BackOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
HttpContextAccessor = httpContextAccessor;
|
||||
EntityService = entityService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IBackOfficeSecurityAccessor" /> instance.
|
||||
/// </summary>
|
||||
protected IBackOfficeSecurityAccessor BackOfficeSecurityAccessor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IHttpContextAccessor" /> instance.
|
||||
/// </summary>
|
||||
protected IHttpContextAccessor HttpContextAccessor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IEntityService" /> instance.
|
||||
/// </summary>
|
||||
protected IEntityService EntityService { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parse a node ID from a string representation found in a querystring value.
|
||||
/// </summary>
|
||||
/// <param name="argument">Querystring value.</param>
|
||||
/// <param name="nodeId">Output parsed Id.</param>
|
||||
/// <returns>True of node ID could be parased, false it not.</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the current user has access to the section
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The user only needs access to one of the sections specified, not all of the sections.
|
||||
/// </remarks>
|
||||
public class SectionHandler : MustSatisfyRequirementAuthorizationHandler<SectionRequirement>
|
||||
{
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SectionHandler" /> class.
|
||||
/// </summary>
|
||||
/// <param name="backOfficeSecurityAccessor">Accessor for back-office security.</param>
|
||||
public SectionHandler(IBackOfficeSecurityAccessor backOfficeSecurityAccessor) =>
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<bool> 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);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// Authorization requirements for <see cref="SectionHandler" />
|
||||
/// </summary>
|
||||
public class SectionRequirement : IAuthorizationRequirement
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SectionRequirement" /> class.
|
||||
/// </summary>
|
||||
/// <param name="aliases">Aliases for sections that the user will need access to.</param>
|
||||
public SectionRequirement(params string[] aliases) => SectionAliases = aliases;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the aliases for sections that the user will need access to.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<string> SectionAliases { get; }
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the current user has access to the section for which the specified tree(s) belongs
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public class TreeHandler : MustSatisfyRequirementAuthorizationHandler<TreeRequirement>
|
||||
{
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
private readonly ITreeService _treeService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TreeHandler" /> class.
|
||||
/// </summary>
|
||||
/// <param name="treeService">Service for section tree operations.</param>
|
||||
/// <param name="backOfficeSecurityAccessor">Accessor for back-office security.</param>
|
||||
public TreeHandler(ITreeService treeService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
{
|
||||
_treeService = treeService ?? throw new ArgumentNullException(nameof(treeService));
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor ??
|
||||
throw new ArgumentNullException(nameof(backOfficeSecurityAccessor));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<bool> 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);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// Authorization requirements for <see cref="TreeHandler" />
|
||||
/// </summary>
|
||||
public class TreeRequirement : IAuthorizationRequirement
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TreeRequirement" /> class.
|
||||
/// </summary>
|
||||
/// <param name="aliases">The aliases for trees that the user will need access to.</param>
|
||||
public TreeRequirement(params string[] aliases) => TreeAliases = aliases;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the aliases for trees that the user will need access to.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<string> TreeAliases { get; }
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user