Files
Umbraco-CMS/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs

190 lines
10 KiB
C#

using System;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Umbraco.Core;
using Umbraco.Core.BackOffice;
using Umbraco.Core.Security;
using Umbraco.Core.Serialization;
using Umbraco.Infrastructure.BackOffice;
using Umbraco.Net;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.BackOffice.Security;
using Umbraco.Web.Common.AspNetCore;
using Umbraco.Web.Common.Security;
namespace Umbraco.Extensions
{
public static class BackOfficeServiceCollectionExtensions
{
/// <summary>
/// Adds the services required for running the Umbraco back office
/// </summary>
/// <param name="services"></param>
public static void AddUmbracoBackOffice(this IServiceCollection services)
{
services.AddAntiforgery();
// TODO: We had this check in v8 where we don't enable these unless we can run...
//if (runtimeState.Level != RuntimeLevel.Upgrade && runtimeState.Level != RuntimeLevel.Run) return app;
services.AddSingleton<IFilterProvider, OverrideAuthorizationFilterProvider>();
services
.AddAuthentication(Constants.Security.BackOfficeAuthenticationType)
.AddCookie(Constants.Security.BackOfficeAuthenticationType)
.AddCookie(Constants.Security.BackOfficeExternalAuthenticationType, o =>
{
o.Cookie.Name = Constants.Security.BackOfficeExternalAuthenticationType;
o.ExpireTimeSpan = TimeSpan.FromMinutes(5);
});
// TODO: Need to add more cookie options, see https://github.com/dotnet/aspnetcore/blob/3.0/src/Identity/Core/src/IdentityServiceCollectionExtensions.cs#L45
services.ConfigureOptions<ConfigureBackOfficeCookieOptions>();
services.AddBackOfficeAuthorizationPolicies();
}
public static void AddUmbracoPreview(this IServiceCollection services)
{
services.AddSignalR();
}
/// <summary>
/// Adds the services required for using Umbraco back office Identity
/// </summary>
/// <param name="services"></param>
public static void AddUmbracoBackOfficeIdentity(this IServiceCollection services)
{
services.AddDataProtection();
services.BuildUmbracoBackOfficeIdentity()
.AddDefaultTokenProviders()
.AddUserStore<BackOfficeUserStore>()
.AddUserManager<IBackOfficeUserManager, BackOfficeUserManager>()
.AddSignInManager<BackOfficeSignInManager>()
.AddClaimsPrincipalFactory<BackOfficeClaimsPrincipalFactory<BackOfficeIdentityUser>>();
// Configure the options specifically for the UmbracoBackOfficeIdentityOptions instance
services.ConfigureOptions<ConfigureBackOfficeIdentityOptions>();
services.ConfigureOptions<ConfigureBackOfficeSecurityStampValidatorOptions>();
}
private static BackOfficeIdentityBuilder BuildUmbracoBackOfficeIdentity(this IServiceCollection services)
{
// Borrowed from https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Extensions.Core/src/IdentityServiceCollectionExtensions.cs#L33
// The reason we need our own is because the Identity system doesn't cater easily for multiple identity systems and particularly being
// able to configure IdentityOptions to a specific provider since there is no named options. So we have strongly typed options
// and strongly typed ILookupNormalizer and IdentityErrorDescriber since those are 'global' and we need to be unintrusive.
// TODO: Could move all of this to BackOfficeComposer?
// Services used by identity
services.TryAddScoped<IUserValidator<BackOfficeIdentityUser>, UserValidator<BackOfficeIdentityUser>>();
services.TryAddScoped<IPasswordValidator<BackOfficeIdentityUser>, PasswordValidator<BackOfficeIdentityUser>>();
services.TryAddScoped<IPasswordHasher<BackOfficeIdentityUser>>(
services => new BackOfficePasswordHasher(
new LegacyPasswordSecurity(),
services.GetRequiredService<IJsonSerializer>()));
services.TryAddScoped<IUserConfirmation<BackOfficeIdentityUser>, DefaultUserConfirmation<BackOfficeIdentityUser>>();
services.TryAddScoped<IUserClaimsPrincipalFactory<BackOfficeIdentityUser>, UserClaimsPrincipalFactory<BackOfficeIdentityUser>>();
// CUSTOM:
services.TryAddScoped<BackOfficeLookupNormalizer>();
services.TryAddScoped<BackOfficeIdentityErrorDescriber>();
services.TryAddScoped<IIpResolver, AspNetCoreIpResolver>();
services.TryAddSingleton<IBackOfficeExternalLoginProviders, NopBackOfficeExternalLoginProviders>();
/*
* IdentityBuilderExtensions.AddUserManager adds UserManager<BackOfficeIdentityUser> to service collection
* To validate the container the following registrations are required (dependencies of UserManager<T>)
* Perhaps we shouldn't be registering UserManager<T> at all and only registering/depending the UmbracoBackOffice prefixed types.
*/
services.TryAddScoped<ILookupNormalizer, BackOfficeLookupNormalizer>();
services.TryAddScoped<IdentityErrorDescriber, BackOfficeIdentityErrorDescriber>();
return new BackOfficeIdentityBuilder(services);
}
private static void AddBackOfficeAuthorizationPolicies(this IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.AddPolicy(AuthorizationPolicies.TreeAccessUsers, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Users)));
options.AddPolicy(AuthorizationPolicies.TreeAccessPartialViews, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.PartialViews)));
options.AddPolicy(AuthorizationPolicies.TreeAccessPartialViewMacros, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.PartialViewMacros)));
options.AddPolicy(AuthorizationPolicies.TreeAccessPackages, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Packages)));
options.AddPolicy(AuthorizationPolicies.TreeAccessLogs, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.LogViewer)));
options.AddPolicy(AuthorizationPolicies.TreeAccessDataTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.DataTypes)));
options.AddPolicy(AuthorizationPolicies.TreeAccessTemplates, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Templates)));
options.AddPolicy(AuthorizationPolicies.TreeAccessMemberTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.MemberTypes)));
options.AddPolicy(AuthorizationPolicies.TreeAccessRelationTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.RelationTypes)));
options.AddPolicy(AuthorizationPolicies.TreeAccessDocumentTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.DocumentTypes)));
options.AddPolicy(AuthorizationPolicies.TreeAccessMemberGroups, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.MemberGroups)));
options.AddPolicy(AuthorizationPolicies.TreeAccessMediaTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.MediaTypes)));
options.AddPolicy(AuthorizationPolicies.TreeAccessMacros, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Macros)));
options.AddPolicy(AuthorizationPolicies.TreeAccessLanguages, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Languages)));
options.AddPolicy(AuthorizationPolicies.TreeAccessDocumentTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Dictionary)));
options.AddPolicy(AuthorizationPolicies.TreeAccessDictionary, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Dictionary, Constants.Trees.Dictionary)));
options.AddPolicy(AuthorizationPolicies.TreeAccessDictionaryOrTemplates, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Dictionary, Constants.Trees.Templates)));
options.AddPolicy(AuthorizationPolicies.TreeAccessDocumentsOrDocumentTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.DocumentTypes, Constants.Trees.Content)));
options.AddPolicy(AuthorizationPolicies.TreeAccessMediaOrMediaTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.MediaTypes, Constants.Trees.Media)));
options.AddPolicy(AuthorizationPolicies.TreeAccessMembersOrMemberTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.MemberTypes, Constants.Trees.Members)));
options.AddPolicy(AuthorizationPolicies.TreeAccessAnySchemaTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.DataTypes, Constants.Trees.DocumentTypes, Constants.Trees.MediaTypes, Constants.Trees.MemberTypes)));
options.AddPolicy(AuthorizationPolicies.TreeAccessAnyContentOrTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(
Constants.Trees.DocumentTypes, Constants.Trees.Content,
Constants.Trees.MediaTypes, Constants.Trees.Media,
Constants.Trees.MemberTypes, Constants.Trees.Members)));
});
services.AddSingleton<IAuthorizationHandler, UmbracoTreeAuthorizeHandler>();
}
}
}