Merge branch 'v13/dev' into v14/dev
# Conflicts: # Directory.Build.props # build/azure-pipelines.yml # src/Umbraco.Cms.Api.Common/DependencyInjection/UmbracoBuilderAuthExtensions.cs # src/Umbraco.Cms.Api.Common/OpenApi/SwaggerRouteTemplatePipelineFilter.cs # src/Umbraco.Cms.Api.Common/Security/Paths.cs # src/Umbraco.Cms.Api.Delivery/Controllers/Security/MemberController.cs # src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs # src/Umbraco.Cms.Api.Delivery/Handlers/InitializeMemberApplicationNotificationHandler.cs # src/Umbraco.Cms.Api.Delivery/Handlers/RevokeMemberAuthenticationTokensNotificationHandler.cs # src/Umbraco.Cms.Api.Delivery/Security/MemberApplicationManager.cs # src/Umbraco.Cms.Api.Delivery/Services/RequestMemberAccessService.cs # src/Umbraco.Core/Constants-OAuthClaims.cs # src/Umbraco.Core/Constants-OAuthClientIds.cs # src/Umbraco.Core/DeliveryApi/IApiContentQueryProvider.cs # src/Umbraco.Core/DeliveryApi/IApiContentQueryService.cs # src/Umbraco.Core/DeliveryApi/IRequestMemberAccessService.cs # src/Umbraco.Core/DeliveryApi/NoopRequestMemberAccessService.cs # src/Umbraco.Core/Models/DeliveryApi/ProtectedAccess.cs # src/Umbraco.Core/Services/ITagService.cs # src/Umbraco.Core/Services/UserService.cs # src/Umbraco.Infrastructure/Security/IMemberApplicationManager.cs # src/Umbraco.Infrastructure/Security/OpenIdDictApplicationManagerBase.cs # src/Umbraco.Web.BackOffice/Controllers/MediaController.cs # tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/HelpPanel/systemInformation.spec.ts # version.json
This commit is contained in:
@@ -9,7 +9,7 @@ public static class FormCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a dictionary object to a query string representation such as:
|
||||
/// firstname=shannon&lastname=deminick
|
||||
/// firstname=shannon&lastname=deminick
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
/// <param name="keysToIgnore">Any keys found in this collection will be removed from the output</param>
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Web.Common.Security;
|
||||
|
||||
namespace Umbraco.Extensions;
|
||||
|
||||
@@ -39,6 +40,17 @@ public static partial class UmbracoApplicationBuilderExtensions
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IUmbracoBuilder SetMemberSignInManager<TSignInManager>(this IUmbracoBuilder builder)
|
||||
where TSignInManager : SignInManager<MemberIdentityUser>, IMemberSignInManager
|
||||
{
|
||||
Type customType = typeof(TSignInManager);
|
||||
Type signInManagerType = typeof(SignInManager<MemberIdentityUser>);
|
||||
builder.Services.Replace(ServiceDescriptor.Scoped(typeof(IMemberSignInManager), customType));
|
||||
builder.Services.AddScoped(customType, services => services.GetRequiredService(signInManagerType));
|
||||
builder.Services.Replace(ServiceDescriptor.Scoped(signInManagerType, customType));
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IUmbracoBuilder SetMemberUserStore<TUserStore>(this IUmbracoBuilder builder)
|
||||
where TUserStore : MemberUserStore
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ public class WebRootFileProviderFactory : ILegacyPackageManifestFileProviderFact
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="IFileProvider" /> instance, pointing at <see cref="WebRootPath"/>.
|
||||
/// Creates a new <see cref="IFileProvider" /> instance, pointing at <see cref="IWebHostEnvironment.WebRootPath"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The newly created <see cref="IFileProvider" /> instance.
|
||||
|
||||
@@ -13,8 +13,8 @@ namespace Umbraco.Cms.Web.Common.Filters;
|
||||
/// Attribute used to check that the request contains a valid Umbraco form request string.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Applying this attribute/filter to a <see cref="SurfaceController"/> or SurfaceController Action will ensure that the Action can only be executed
|
||||
/// when it is routed to from within Umbraco, typically when rendering a form with BeginUmbracoForm. It will mean that the natural MVC route for this Action
|
||||
/// Applying this attribute/filter to a <see cref="T:Umbraco.Cms.Web.Website.Controllers.SurfaceContorller"/> or SurfaceController Action will ensure that the Action can only be executed
|
||||
/// when it is routed to from within Umbraco, typically when rendering a form with BeginUmbracoForm. It will mean that the natural MVC route for this Action
|
||||
/// will fail with a <see cref="HttpUmbracoFormRouteStringException" />.
|
||||
/// </remarks>
|
||||
public class ValidateUmbracoFormRouteStringAttribute : TypeFilterAttribute
|
||||
|
||||
@@ -114,8 +114,6 @@ public static class UmbracoBuilderDependencyInjectionExtensions
|
||||
builder.AddInMemoryModelsRazorEngine();
|
||||
|
||||
builder.AddNotificationHandler<ModelBindingErrorNotification, ModelsBuilderNotificationHandler>();
|
||||
builder.AddNotificationHandler<ContentTypeCacheRefresherNotification, OutOfDateModelsStatus>();
|
||||
builder.AddNotificationHandler<DataTypeCacheRefresherNotification, OutOfDateModelsStatus>();
|
||||
}
|
||||
|
||||
if (builder.Config.GetRuntimeMode() != RuntimeMode.Production)
|
||||
@@ -128,12 +126,16 @@ public static class UmbracoBuilderDependencyInjectionExtensions
|
||||
builder.AddNotificationHandler<UmbracoRequestEndNotification, AutoModelsNotificationHandler>();
|
||||
builder.AddNotificationHandler<ContentTypeCacheRefresherNotification, AutoModelsNotificationHandler>();
|
||||
builder.AddNotificationHandler<DataTypeCacheRefresherNotification, AutoModelsNotificationHandler>();
|
||||
|
||||
builder.AddNotificationHandler<ContentTypeCacheRefresherNotification, OutOfDateModelsStatus>();
|
||||
builder.AddNotificationHandler<DataTypeCacheRefresherNotification, OutOfDateModelsStatus>();
|
||||
}
|
||||
|
||||
builder.Services.TryAddSingleton<IModelsBuilderDashboardProvider, NoopModelsBuilderDashboardProvider>();
|
||||
|
||||
// Register required services for ModelsBuilderDashboardController
|
||||
builder.Services.AddSingleton<IModelsGenerator, ModelsGenerator>();
|
||||
|
||||
// TODO: Remove in v13 - this is only here in case someone is already using this generator directly
|
||||
builder.Services.AddSingleton<ModelsGenerator>();
|
||||
builder.Services.AddSingleton<OutOfDateModelsStatus>();
|
||||
@@ -163,6 +165,7 @@ public static class UmbracoBuilderDependencyInjectionExtensions
|
||||
|
||||
// This is what the community MB would replace, all of the above services are fine to be registered
|
||||
builder.Services.AddSingleton<IPublishedModelFactory>(factory => factory.CreateDefaultPublishedModelFactory());
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,44 @@
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.Security;
|
||||
|
||||
public class ConfigureSecurityStampOptions : IConfigureOptions<SecurityStampValidatorOptions>
|
||||
{
|
||||
private readonly SecuritySettings _securitySettings;
|
||||
|
||||
public ConfigureSecurityStampOptions()
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<IOptions<SecuritySettings>>())
|
||||
{
|
||||
}
|
||||
|
||||
public ConfigureSecurityStampOptions(IOptions<SecuritySettings> securitySettings)
|
||||
=> _securitySettings = securitySettings.Value;
|
||||
|
||||
[Obsolete("Use the overload accepting SecuritySettings instead. Scheduled for removal in v14.")]
|
||||
public static void ConfigureOptions(SecurityStampValidatorOptions options)
|
||||
=> ConfigureOptions(options, StaticServiceProvider.Instance.GetRequiredService<SecuritySettings>());
|
||||
|
||||
/// <summary>
|
||||
/// Configures security stamp options and ensures any custom claims
|
||||
/// set on the identity are persisted to the new identity when it's refreshed.
|
||||
/// </summary>
|
||||
/// <param name="options"></param>
|
||||
public static void ConfigureOptions(SecurityStampValidatorOptions options)
|
||||
/// <param name="options">Options for <see cref="ISecurityStampValidator"/>.</param>
|
||||
/// <param name="securitySettings">The <see cref="SecuritySettings" /> options.</param>
|
||||
public static void ConfigureOptions(SecurityStampValidatorOptions options, SecuritySettings securitySettings)
|
||||
{
|
||||
options.ValidationInterval = TimeSpan.FromMinutes(30);
|
||||
// Adjust the security stamp validation interval to a shorter duration
|
||||
// when concurrent logins are not allowed and the duration has the default interval value
|
||||
// (currently defaults to 30 minutes), ensuring quicker re-validation.
|
||||
if (securitySettings.AllowConcurrentLogins is false && options.ValidationInterval == TimeSpan.FromMinutes(30))
|
||||
{
|
||||
options.ValidationInterval = TimeSpan.FromSeconds(30);
|
||||
}
|
||||
|
||||
// When refreshing the principal, ensure custom claims that
|
||||
// might have been set with an external identity continue
|
||||
@@ -34,6 +58,7 @@ public class ConfigureSecurityStampOptions : IConfigureOptions<SecurityStampVali
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Configure(SecurityStampValidatorOptions options)
|
||||
=> ConfigureOptions(options);
|
||||
=> ConfigureOptions(options, _securitySettings);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
@@ -30,14 +31,40 @@ public class MemberSignInManager : UmbracoSignInManager<MemberIdentityUser>, IMe
|
||||
IAuthenticationSchemeProvider schemes,
|
||||
IUserConfirmation<MemberIdentityUser> confirmation,
|
||||
IMemberExternalLoginProviders memberExternalLoginProviders,
|
||||
IEventAggregator eventAggregator)
|
||||
: base(memberManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation)
|
||||
IEventAggregator eventAggregator,
|
||||
IOptions<SecuritySettings> securitySettings)
|
||||
: base(memberManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation, securitySettings)
|
||||
{
|
||||
_memberExternalLoginProviders = memberExternalLoginProviders;
|
||||
_eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
[Obsolete("Use ctor with all params")]
|
||||
[Obsolete("Use non-obsolete constructor. This is scheduled for removal in V14.")]
|
||||
public MemberSignInManager(
|
||||
UserManager<MemberIdentityUser> memberManager,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
IUserClaimsPrincipalFactory<MemberIdentityUser> claimsFactory,
|
||||
IOptions<IdentityOptions> optionsAccessor,
|
||||
ILogger<SignInManager<MemberIdentityUser>> logger,
|
||||
IAuthenticationSchemeProvider schemes,
|
||||
IUserConfirmation<MemberIdentityUser> confirmation,
|
||||
IMemberExternalLoginProviders memberExternalLoginProviders,
|
||||
IEventAggregator eventAggregator)
|
||||
: this(
|
||||
memberManager,
|
||||
contextAccessor,
|
||||
claimsFactory,
|
||||
optionsAccessor,
|
||||
logger,
|
||||
schemes,
|
||||
confirmation,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IMemberExternalLoginProviders>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IEventAggregator>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IOptions<SecuritySettings>>())
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Use non-obsolete constructor. This is scheduled for removal in V14.")]
|
||||
public MemberSignInManager(
|
||||
UserManager<MemberIdentityUser> memberManager,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
|
||||
@@ -2,26 +2,32 @@ using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Abstract sign in manager implementation allowing modifying all defeault authentication schemes
|
||||
/// Abstract sign in manager implementation allowing modifying all default authentication schemes.
|
||||
/// </summary>
|
||||
/// <typeparam name="TUser"></typeparam>
|
||||
public abstract class UmbracoSignInManager<TUser> : SignInManager<TUser>
|
||||
where TUser : UmbracoIdentityUser
|
||||
{
|
||||
private SecuritySettings _securitySettings;
|
||||
|
||||
// borrowed from https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs
|
||||
protected const string UmbracoSignInMgrLoginProviderKey = "LoginProvider";
|
||||
|
||||
// borrowed from https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs
|
||||
protected const string UmbracoSignInMgrXsrfKey = "XsrfId";
|
||||
|
||||
[Obsolete("Use non-obsolete constructor. This is scheduled for removal in V14.")]
|
||||
public UmbracoSignInManager(
|
||||
UserManager<TUser> userManager,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
@@ -30,8 +36,30 @@ public abstract class UmbracoSignInManager<TUser> : SignInManager<TUser>
|
||||
ILogger<SignInManager<TUser>> logger,
|
||||
IAuthenticationSchemeProvider schemes,
|
||||
IUserConfirmation<TUser> confirmation)
|
||||
: this(
|
||||
userManager,
|
||||
contextAccessor,
|
||||
claimsFactory,
|
||||
optionsAccessor,
|
||||
logger,
|
||||
schemes,
|
||||
confirmation,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IOptions<SecuritySettings>>())
|
||||
{
|
||||
}
|
||||
|
||||
public UmbracoSignInManager(
|
||||
UserManager<TUser> userManager,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
IUserClaimsPrincipalFactory<TUser> claimsFactory,
|
||||
IOptions<IdentityOptions> optionsAccessor,
|
||||
ILogger<SignInManager<TUser>> logger,
|
||||
IAuthenticationSchemeProvider schemes,
|
||||
IUserConfirmation<TUser> confirmation,
|
||||
IOptions<SecuritySettings> securitySettingsOptions)
|
||||
: base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation)
|
||||
{
|
||||
_securitySettings = securitySettingsOptions.Value;
|
||||
}
|
||||
|
||||
protected abstract string AuthenticationType { get; }
|
||||
@@ -47,7 +75,7 @@ public abstract class UmbracoSignInManager<TUser> : SignInManager<TUser>
|
||||
{
|
||||
// override to handle logging/events
|
||||
SignInResult result = await base.PasswordSignInAsync(user, password, isPersistent, lockoutOnFailure);
|
||||
return await HandleSignIn(user, user.UserName, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -340,6 +368,11 @@ public abstract class UmbracoSignInManager<TUser> : SignInManager<TUser>
|
||||
|
||||
await UserManager.UpdateAsync(user);
|
||||
|
||||
if (_securitySettings.AllowConcurrentLogins is false)
|
||||
{
|
||||
await UserManager.UpdateSecurityStampAsync(user);
|
||||
}
|
||||
|
||||
Logger.LogInformation("User: {UserName} logged in from IP address {IpAddress}", username, Context.Connection.RemoteIpAddress);
|
||||
}
|
||||
else if (result.IsLockedOut)
|
||||
|
||||
Reference in New Issue
Block a user