Removed use of Microsoft.AspNet.Identity.Owin, but had to port across some OWIN extensions

This commit is contained in:
Scott Brady
2020-04-20 20:14:36 +01:00
parent 11f5c98206
commit 96d9d3bd21
10 changed files with 195 additions and 18 deletions

View File

@@ -88,7 +88,6 @@
<PackageReference Include="ClientDependency-Mvc5" Version="1.9.3" />
<PackageReference Include="ImageProcessor.Web" Version="4.10.0.100" />
<PackageReference Include="ImageProcessor.Web.Config" Version="2.5.0.100" />
<PackageReference Include="Microsoft.AspNet.Identity.Owin" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNet.Mvc" Version="5.2.7" />
<PackageReference Include="Microsoft.AspNet.WebApi" Version="5.2.7" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.10.0" />

View File

@@ -374,7 +374,7 @@ namespace Umbraco.Web.Editors
return await ExternalSignInAsync(loginInfo, externalSignInResponse);
}
private async Task<ActionResult> ExternalSignInAsync(ExternalLoginInfo2 loginInfo, Func<ActionResult> response)
private async Task<ActionResult> ExternalSignInAsync(ExternalLoginInfo loginInfo, Func<ActionResult> response)
{
if (loginInfo == null) throw new ArgumentNullException("loginInfo");
if (response == null) throw new ArgumentNullException("response");
@@ -437,7 +437,7 @@ namespace Umbraco.Web.Editors
return response();
}
private async Task<bool> AutoLinkAndSignInExternalAccount(ExternalLoginInfo2 loginInfo, ExternalSignInAutoLinkOptions autoLinkOptions)
private async Task<bool> AutoLinkAndSignInExternalAccount(ExternalLoginInfo loginInfo, ExternalSignInAutoLinkOptions autoLinkOptions)
{
if (autoLinkOptions == null)
return false;

View File

@@ -21,6 +21,7 @@ using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.Runtime;
using Umbraco.Core.WebAssets;
using Umbraco.Web.Security;
namespace Umbraco.Web.Editors
{

View File

@@ -15,6 +15,7 @@ using Umbraco.Web.Composing;
using Umbraco.Web.Editors;
using Umbraco.Web.Features;
using Umbraco.Web.Models;
using Umbraco.Web.Security;
using Umbraco.Web.Trees;
using Umbraco.Web.WebAssets;

View File

@@ -90,6 +90,12 @@ namespace Umbraco.Web
return context.Get<T>(GetKey(typeof(T)));
}
public static IOwinContext Set<T>(this IOwinContext context, T value)
{
if (context == null) throw new ArgumentNullException(nameof(context));
return context.Set(GetKey(typeof(T)), value);
}
private static string GetKey(Type t)
{
return "AspNet.Identity.Owin:" + t.AssemblyQualifiedName;

View File

@@ -2,6 +2,7 @@
using System.Threading;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Owin;
using Microsoft.Owin.Extensions;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
@@ -14,11 +15,8 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.Mapping;
using Umbraco.Core.Models.Identity;
using Umbraco.Net;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Web.Composing;
using Umbraco.Web.Models.Identity;
@@ -421,5 +419,41 @@ namespace Umbraco.Web.Security
return authOptions;
}
public static IAppBuilder CreatePerOwinContext<T>(this IAppBuilder app, Func<T> createCallback)
where T : class, IDisposable
{
return CreatePerOwinContext<T>(app, (options, context) => createCallback());
}
public static IAppBuilder CreatePerOwinContext<T>(this IAppBuilder app,
Func<IdentityFactoryOptions<T>, IOwinContext, T> createCallback) where T : class, IDisposable
{
if (app == null)
{
throw new ArgumentNullException("app");
}
return app.CreatePerOwinContext(createCallback, (options, instance) => instance.Dispose());
}
public static IAppBuilder CreatePerOwinContext<T>(this IAppBuilder app,
Func<IdentityFactoryOptions<T>, IOwinContext, T> createCallback,
Action<IdentityFactoryOptions<T>, T> disposeCallback) where T : class, IDisposable
{
if (app == null) throw new ArgumentNullException(nameof(app));
if (createCallback == null) throw new ArgumentNullException(nameof(createCallback));
if (disposeCallback == null) throw new ArgumentNullException(nameof(disposeCallback));
app.Use(typeof(IdentityFactoryMiddleware<T, IdentityFactoryOptions<T>>),
new IdentityFactoryOptions<T>
{
DataProtectionProvider = app.GetDataProtectionProvider(),
Provider = new IdentityFactoryProvider<T>
{
OnCreate = createCallback,
OnDispose = disposeCallback
}
});
return app;
}
}
}

View File

@@ -1,14 +1,16 @@
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
using UserLoginInfo = Microsoft.AspNetCore.Identity.UserLoginInfo;
namespace Umbraco.Web.Security
{
public static class AuthenticationManagerExtensions
{
private static ExternalLoginInfo2 GetExternalLoginInfo(AuthenticateResult result)
private static ExternalLoginInfo GetExternalLoginInfo(AuthenticateResult result)
{
if (result == null || result.Identity == null)
{
@@ -27,7 +29,7 @@ namespace Umbraco.Web.Security
}
var email = result.Identity.FindFirst(ClaimTypes.Email)?.Value;
return new ExternalLoginInfo2
return new ExternalLoginInfo
{
ExternalIdentity = result.Identity,
Login = new UserLoginInfo(idClaim.Issuer, idClaim.Value, idClaim.Issuer),
@@ -47,7 +49,7 @@ namespace Umbraco.Web.Security
/// dictionary
/// </param>
/// <returns></returns>
public static async Task<ExternalLoginInfo2> GetExternalLoginInfoAsync(this IAuthenticationManager manager,
public static async Task<ExternalLoginInfo> GetExternalLoginInfoAsync(this IAuthenticationManager manager,
string authenticationType,
string xsrfKey, string expectedValue)
{
@@ -74,7 +76,7 @@ namespace Umbraco.Web.Security
/// <param name="manager"></param>
/// <param name="authenticationType"></param>
/// <returns></returns>
public static async Task<ExternalLoginInfo2> GetExternalLoginInfoAsync(this IAuthenticationManager manager, string authenticationType)
public static async Task<ExternalLoginInfo> GetExternalLoginInfoAsync(this IAuthenticationManager manager, string authenticationType)
{
if (manager == null)
{
@@ -82,9 +84,25 @@ namespace Umbraco.Web.Security
}
return GetExternalLoginInfo(await manager.AuthenticateAsync(authenticationType));
}
public static IEnumerable<AuthenticationDescription> GetExternalAuthenticationTypes(this IAuthenticationManager manager)
{
if (manager == null) throw new ArgumentNullException(nameof(manager));
return manager.GetAuthenticationTypes(d => d.Properties != null && d.Properties.ContainsKey("Caption"));
}
public static ClaimsIdentity CreateTwoFactorRememberBrowserIdentity(this IAuthenticationManager manager, string userId)
{
if (manager == null) throw new ArgumentNullException(nameof(manager));
var rememberBrowserIdentity = new ClaimsIdentity(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
rememberBrowserIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId));
return rememberBrowserIdentity;
}
}
public class ExternalLoginInfo2
public class ExternalLoginInfo
{
/// <summary>Associated login data</summary>
public UserLoginInfo Login { get; set; }

View File

@@ -31,13 +31,13 @@ namespace Umbraco.Web.Security
/// <summary>
/// A callback executed during account auto-linking and before the user is persisted
/// </summary>
public Action<BackOfficeIdentityUser, ExternalLoginInfo2> OnAutoLinking { get; set; }
public Action<BackOfficeIdentityUser, ExternalLoginInfo> OnAutoLinking { get; set; }
/// <summary>
/// A callback executed during every time a user authenticates using an external login.
/// returns a boolean indicating if sign in should continue or not.
/// </summary>
public Func<BackOfficeIdentityUser, ExternalLoginInfo2, bool> OnExternalLogin { get; set; }
public Func<BackOfficeIdentityUser, ExternalLoginInfo, bool> OnExternalLogin { get; set; }
/// <summary>B
@@ -46,7 +46,7 @@ namespace Umbraco.Web.Security
/// <param name="umbracoContext"></param>
/// <param name="loginInfo"></param>
/// <returns></returns>
public string[] GetDefaultUserGroups(IUmbracoContext umbracoContext, ExternalLoginInfo2 loginInfo)
public string[] GetDefaultUserGroups(IUmbracoContext umbracoContext, ExternalLoginInfo loginInfo)
{
return _defaultUserGroups;
}
@@ -59,7 +59,7 @@ namespace Umbraco.Web.Security
///
/// For public auth providers this should always be false!!!
/// </summary>
public bool ShouldAutoLinkExternalAccount(IUmbracoContext umbracoContext, ExternalLoginInfo2 loginInfo)
public bool ShouldAutoLinkExternalAccount(IUmbracoContext umbracoContext, ExternalLoginInfo loginInfo)
{
return _autoLinkExternalAccount;
}
@@ -69,7 +69,7 @@ namespace Umbraco.Web.Security
/// <summary>
/// The default Culture to use for auto-linking users
/// </summary>
public string GetDefaultCulture(IUmbracoContext umbracoContext, ExternalLoginInfo2 loginInfo)
public string GetDefaultCulture(IUmbracoContext umbracoContext, ExternalLoginInfo loginInfo)
{
return _defaultCulture;
}

View File

@@ -0,0 +1,117 @@
using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Microsoft.Owin.Security.DataProtection;
namespace Umbraco.Web.Security
{
/// <summary>
/// Adapted from Microsoft.AspNet.Identity.Owin.IdentityFactoryMiddleware
/// </summary>
public class IdentityFactoryMiddleware<TResult, TOptions> : OwinMiddleware
where TResult : class, IDisposable
where TOptions : IdentityFactoryOptions<TResult>
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="next">The next middleware in the OWIN pipeline to invoke</param>
/// <param name="options">Configuration options for the middleware</param>
public IdentityFactoryMiddleware(OwinMiddleware next, TOptions options)
: base(next)
{
if (options == null)
{
throw new ArgumentNullException("options");
}
if (options.Provider == null)
{
throw new ArgumentNullException("options.Provider");
}
Options = options;
}
/// <summary>
/// Configuration options
/// </summary>
public TOptions Options { get; private set; }
/// <summary>
/// Create an object using the Options.Provider, storing it in the OwinContext and then disposes the object when finished
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override async Task Invoke(IOwinContext context)
{
var instance = Options.Provider.Create(Options, context);
try
{
context.Set(instance);
if (Next != null)
{
await Next.Invoke(context);
}
}
finally
{
Options.Provider.Dispose(Options, instance);
}
}
}
public class IdentityFactoryOptions<T> where T : class, IDisposable
{
/// <summary>
/// Used to configure the data protection provider
/// </summary>
public IDataProtectionProvider DataProtectionProvider { get; set; }
/// <summary>
/// Provider used to Create and Dispose objects
/// </summary>
public IdentityFactoryProvider<T> Provider { get; set; }
}
public class IdentityFactoryProvider<T> where T : class, IDisposable
{
/// <summary>
/// Constructor
/// </summary>
public IdentityFactoryProvider()
{
OnDispose = (options, instance) => { };
OnCreate = (options, context) => null;
}
/// <summary>
/// A delegate assigned to this property will be invoked when the related method is called
/// </summary>
public Func<IdentityFactoryOptions<T>, IOwinContext, T> OnCreate { get; set; }
/// <summary>
/// A delegate assigned to this property will be invoked when the related method is called
/// </summary>
public Action<IdentityFactoryOptions<T>, T> OnDispose { get; set; }
/// <summary>
/// Calls the OnCreate Delegate
/// </summary>
/// <param name="options"></param>
/// <param name="context"></param>
/// <returns></returns>
public virtual T Create(IdentityFactoryOptions<T> options, IOwinContext context)
{
return OnCreate(options, context);
}
/// <summary>
/// Calls the OnDispose delegate
/// </summary>
/// <param name="options"></param>
/// <param name="instance"></param>
public virtual void Dispose(IdentityFactoryOptions<T> options, T instance)
{
OnDispose(options, instance);
}
}
}

View File

@@ -77,7 +77,7 @@
</PackageReference>
<PackageReference Include="LightInject.WebApi" Version="2.0.0" />
<PackageReference Include="Markdown" Version="2.2.1" />
<PackageReference Include="Microsoft.AspNet.Identity.Owin" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNet.Identity.Core" Version="2.2.3" />
<PackageReference Include="Microsoft.AspNet.Mvc" Version="5.2.7" />
<PackageReference Include="Microsoft.AspNet.SignalR.Core" Version="2.4.0" />
<PackageReference Include="Microsoft.AspNet.WebApi" Version="5.2.7" />
@@ -147,6 +147,7 @@
<Compile Include="Compose\BackOfficeUserAuditEventsComposer.cs" />
<Compile Include="Composing\CompositionExtensions\Installer.cs" />
<Compile Include="Composing\LightInject\LightInjectContainer.cs" />
<Compile Include="Security\IdentityFactoryMiddleware.cs" />
<Compile Include="WebAssets\CDF\ClientDependencyRuntimeMinifier.cs" />
<Compile Include="Models\NoNodesViewModel.cs" />
<Compile Include="Mvc\RenderNoContentController.cs" />