2020-04-06 17:23:04 +01:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Security.Claims;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using Microsoft.Owin.Security.Cookies;
|
|
|
|
|
|
using Umbraco.Core;
|
|
|
|
|
|
using Umbraco.Web.Models.Identity;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Web.Security
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Adapted from Microsoft.AspNet.Identity.Owin.SecurityStampValidator
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class UmbracoSecurityStampValidator
|
|
|
|
|
|
{
|
2020-04-20 19:45:08 +01:00
|
|
|
|
public static Func<CookieValidateIdentityContext, Task> OnValidateIdentity<TSignInManager, TManager, TUser>(
|
2020-04-06 17:23:04 +01:00
|
|
|
|
TimeSpan validateInterval,
|
|
|
|
|
|
Func<BackOfficeSignInManager, TManager, TUser, Task<ClaimsIdentity>> regenerateIdentityCallback,
|
|
|
|
|
|
Func<ClaimsIdentity, string> getUserIdCallback)
|
2020-04-20 19:45:08 +01:00
|
|
|
|
where TSignInManager : BackOfficeSignInManager
|
2020-04-06 17:23:04 +01:00
|
|
|
|
where TManager : BackOfficeUserManager<TUser>
|
|
|
|
|
|
where TUser : BackOfficeIdentityUser
|
|
|
|
|
|
{
|
|
|
|
|
|
if (getUserIdCallback == null) throw new ArgumentNullException(nameof(getUserIdCallback));
|
|
|
|
|
|
|
|
|
|
|
|
return async context =>
|
|
|
|
|
|
{
|
2020-04-30 08:50:56 +01:00
|
|
|
|
var currentUtc = context.Options?.SystemClock?.UtcNow ?? DateTimeOffset.UtcNow;
|
2020-04-06 17:23:04 +01:00
|
|
|
|
|
|
|
|
|
|
var issuedUtc = context.Properties.IssuedUtc;
|
|
|
|
|
|
|
|
|
|
|
|
// Only validate if enough time has elapsed
|
|
|
|
|
|
var validate = issuedUtc == null;
|
|
|
|
|
|
if (issuedUtc != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var timeElapsed = currentUtc.Subtract(issuedUtc.Value);
|
|
|
|
|
|
validate = timeElapsed > validateInterval;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (validate)
|
|
|
|
|
|
{
|
|
|
|
|
|
var manager = context.OwinContext.Get<TManager>();
|
2020-04-20 19:45:08 +01:00
|
|
|
|
if (manager == null) throw new InvalidOperationException("Unable to load BackOfficeUserManager");
|
|
|
|
|
|
|
|
|
|
|
|
var signInManager = context.OwinContext.Get<TSignInManager>();
|
|
|
|
|
|
if (signInManager == null) throw new InvalidOperationException("Unable to load BackOfficeSignInManager");
|
2020-04-06 17:23:04 +01:00
|
|
|
|
|
|
|
|
|
|
var userId = getUserIdCallback(context.Identity);
|
|
|
|
|
|
|
2020-04-20 19:45:08 +01:00
|
|
|
|
if (userId != null)
|
2020-04-06 17:23:04 +01:00
|
|
|
|
{
|
|
|
|
|
|
var user = await manager.FindByIdAsync(userId);
|
|
|
|
|
|
var reject = true;
|
|
|
|
|
|
|
|
|
|
|
|
// Refresh the identity if the stamp matches, otherwise reject
|
|
|
|
|
|
if (user != null && manager.SupportsUserSecurityStamp)
|
|
|
|
|
|
{
|
2020-04-30 09:27:34 +01:00
|
|
|
|
var securityStamp = context.Identity.FindFirstValue(Constants.Web.SecurityStampClaimType);
|
2020-04-20 19:45:08 +01:00
|
|
|
|
var newSecurityStamp = await manager.GetSecurityStampAsync(user);
|
|
|
|
|
|
|
|
|
|
|
|
if (securityStamp == newSecurityStamp)
|
2020-04-06 17:23:04 +01:00
|
|
|
|
{
|
|
|
|
|
|
reject = false;
|
|
|
|
|
|
// Regenerate fresh claims if possible and resign in
|
|
|
|
|
|
if (regenerateIdentityCallback != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var identity = await regenerateIdentityCallback.Invoke(signInManager, manager, user);
|
|
|
|
|
|
if (identity != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Fix for regression where this value is not updated
|
|
|
|
|
|
// Setting it to null so that it is refreshed by the cookie middleware
|
|
|
|
|
|
context.Properties.IssuedUtc = null;
|
|
|
|
|
|
context.Properties.ExpiresUtc = null;
|
|
|
|
|
|
context.OwinContext.Authentication.SignIn(context.Properties, identity);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (reject)
|
|
|
|
|
|
{
|
|
|
|
|
|
context.RejectIdentity();
|
|
|
|
|
|
context.OwinContext.Authentication.SignOut(context.Options.AuthenticationType);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|