Updates the UmbracoBackOfficeIdentity to have better support for claims and adds unit tests for it. Creates OwinLogger's and methods to apply them. Updates security methods to ensure that a UmbracoBackOfficeIdentity is returned even from a normal ClaimsIdentity which will be the case with bearer tokens. Updates the angular anti-forgery checker to be ignore if the auth type is not cookie based. Adds a simple token server provider that people can use if they want. Now token authentication is working.
This commit is contained in:
@@ -37,9 +37,7 @@
|
|||||||
|
|
||||||
public const string StartContentNodeIdClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/startcontentnode";
|
public const string StartContentNodeIdClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/startcontentnode";
|
||||||
public const string StartMediaNodeIdClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/startmedianode";
|
public const string StartMediaNodeIdClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/startmedianode";
|
||||||
public const string AllowedApplicationsClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/allowedapps";
|
public const string AllowedApplicationsClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/allowedapp";
|
||||||
//public const string UserIdClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/userid";
|
|
||||||
public const string CultureClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/culture";
|
|
||||||
public const string SessionIdClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/sessionid";
|
public const string SessionIdClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/sessionid";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
63
src/Umbraco.Core/Logging/OwinLogger.cs
Normal file
63
src/Umbraco.Core/Logging/OwinLogger.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Umbraco.Core.Logging
|
||||||
|
{
|
||||||
|
internal class OwinLogger : Microsoft.Owin.Logging.ILogger
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly Lazy<Type> _type;
|
||||||
|
|
||||||
|
public OwinLogger(ILogger logger, Lazy<Type> type)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Aggregates most logging patterns to a single method. This must be compatible with the Func representation in the OWIN environment.
|
||||||
|
/// To check IsEnabled call WriteCore with only TraceEventType and check the return value, no event will be written.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventType"/><param name="eventId"/><param name="state"/><param name="exception"/><param name="formatter"/>
|
||||||
|
/// <returns/>
|
||||||
|
public bool WriteCore(TraceEventType eventType, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)
|
||||||
|
{
|
||||||
|
if (state == null) state = "";
|
||||||
|
switch (eventType)
|
||||||
|
{
|
||||||
|
case TraceEventType.Critical:
|
||||||
|
_logger.Error(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state), exception ?? new Exception("Critical error"));
|
||||||
|
return true;
|
||||||
|
case TraceEventType.Error:
|
||||||
|
_logger.Error(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state), exception ?? new Exception("Error"));
|
||||||
|
return true;
|
||||||
|
case TraceEventType.Warning:
|
||||||
|
_logger.Warn(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state));
|
||||||
|
return true;
|
||||||
|
case TraceEventType.Information:
|
||||||
|
_logger.Info(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state));
|
||||||
|
return true;
|
||||||
|
case TraceEventType.Verbose:
|
||||||
|
_logger.Debug(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state));
|
||||||
|
return true;
|
||||||
|
case TraceEventType.Start:
|
||||||
|
_logger.Debug(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state));
|
||||||
|
return true;
|
||||||
|
case TraceEventType.Stop:
|
||||||
|
_logger.Debug(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state));
|
||||||
|
return true;
|
||||||
|
case TraceEventType.Suspend:
|
||||||
|
_logger.Debug(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state));
|
||||||
|
return true;
|
||||||
|
case TraceEventType.Resume:
|
||||||
|
_logger.Debug(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state));
|
||||||
|
return true;
|
||||||
|
case TraceEventType.Transfer:
|
||||||
|
_logger.Debug(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state));
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException("eventType");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/Umbraco.Core/Logging/OwinLoggerFactory.cs
Normal file
24
src/Umbraco.Core/Logging/OwinLoggerFactory.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Owin.Logging;
|
||||||
|
|
||||||
|
namespace Umbraco.Core.Logging
|
||||||
|
{
|
||||||
|
internal class OwinLoggerFactory : ILoggerFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new ILogger instance of the given name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"/>
|
||||||
|
/// <returns/>
|
||||||
|
public Microsoft.Owin.Logging.ILogger Create(string name)
|
||||||
|
{
|
||||||
|
return new OwinLogger(
|
||||||
|
LoggerResolver.HasCurrent ? LoggerResolver.Current.Logger : new DebugDiagnosticsLogger(),
|
||||||
|
new Lazy<Type>(() => Type.GetType(name) ?? typeof (OwinLogger)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ using System.Collections.Specialized;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
using System.Security.Claims;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
@@ -14,6 +15,7 @@ using Microsoft.Owin;
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Umbraco.Core.Configuration;
|
using Umbraco.Core.Configuration;
|
||||||
using Umbraco.Core.Models.Membership;
|
using Umbraco.Core.Models.Membership;
|
||||||
|
using Microsoft.Owin;
|
||||||
|
|
||||||
namespace Umbraco.Core.Security
|
namespace Umbraco.Core.Security
|
||||||
{
|
{
|
||||||
@@ -95,8 +97,14 @@ namespace Umbraco.Core.Security
|
|||||||
{
|
{
|
||||||
if (http == null) throw new ArgumentNullException("http");
|
if (http == null) throw new ArgumentNullException("http");
|
||||||
if (http.User == null) return null; //there's no user at all so no identity
|
if (http.User == null) return null; //there's no user at all so no identity
|
||||||
var identity = http.User.Identity as UmbracoBackOfficeIdentity;
|
|
||||||
if (identity != null) return identity;
|
//If it's already a UmbracoBackOfficeIdentity
|
||||||
|
var backOfficeIdentity = http.User.Identity as UmbracoBackOfficeIdentity;
|
||||||
|
if (backOfficeIdentity != null) return backOfficeIdentity;
|
||||||
|
|
||||||
|
//Otherwise convert to a UmbracoBackOfficeIdentity
|
||||||
|
var claimsIdentity = http.User.Identity as ClaimsIdentity;
|
||||||
|
if (claimsIdentity != null) return UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity);
|
||||||
|
|
||||||
if (authenticateRequestIfNotFound == false) return null;
|
if (authenticateRequestIfNotFound == false) return null;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Linq;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Identity;
|
using Microsoft.AspNet.Identity;
|
||||||
@@ -17,17 +18,19 @@ namespace Umbraco.Core.Security
|
|||||||
{
|
{
|
||||||
var baseIdentity = await base.CreateAsync(manager, user, authenticationType);
|
var baseIdentity = await base.CreateAsync(manager, user, authenticationType);
|
||||||
|
|
||||||
var umbracoIdentity = new UmbracoBackOfficeIdentity(baseIdentity, new UserData()
|
var umbracoIdentity = new UmbracoBackOfficeIdentity(baseIdentity,
|
||||||
{
|
//set a new session id
|
||||||
Id = user.Id,
|
new UserData(Guid.NewGuid().ToString("N"))
|
||||||
Username = user.UserName,
|
{
|
||||||
RealName = user.Name,
|
Id = user.Id,
|
||||||
AllowedApplications = user.AllowedSections,
|
Username = user.UserName,
|
||||||
Culture = user.Culture,
|
RealName = user.Name,
|
||||||
Roles = user.Roles.Select(x => x.RoleId).ToArray(),
|
AllowedApplications = user.AllowedSections,
|
||||||
StartContentNode = user.StartContentId,
|
Culture = user.Culture,
|
||||||
StartMediaNode = user.StartMediaId
|
Roles = user.Roles.Select(x => x.RoleId).ToArray(),
|
||||||
});
|
StartContentNode = user.StartContentId,
|
||||||
|
StartMediaNode = user.StartMediaId
|
||||||
|
});
|
||||||
|
|
||||||
return umbracoIdentity;
|
return umbracoIdentity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using System.Web.Security;
|
using System.Web.Security;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Umbraco.Core.Configuration;
|
||||||
|
|
||||||
namespace Umbraco.Core.Security
|
namespace Umbraco.Core.Security
|
||||||
{
|
{
|
||||||
@@ -19,6 +23,57 @@ namespace Umbraco.Core.Security
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class UmbracoBackOfficeIdentity : FormsIdentity
|
public class UmbracoBackOfficeIdentity : FormsIdentity
|
||||||
{
|
{
|
||||||
|
public static UmbracoBackOfficeIdentity FromClaimsIdentity(ClaimsIdentity identity)
|
||||||
|
{
|
||||||
|
foreach (var t in RequiredBackOfficeIdentityClaimTypes)
|
||||||
|
{
|
||||||
|
//if the identity doesn't have the claim, or the claim value is null
|
||||||
|
if (identity.HasClaim(x => x.Type == t) == false || identity.HasClaim(x => x.Type == t && x.Value.IsNullOrWhiteSpace()))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Cannot create a " + typeof(UmbracoBackOfficeIdentity) + " from " + typeof(ClaimsIdentity) + " since the required claim " + t + " is missing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var username = identity.GetUserName();
|
||||||
|
var session = identity.FindFirstValue(Constants.Security.SessionIdClaimType);
|
||||||
|
var startContentId = identity.FindFirstValue(Constants.Security.StartContentNodeIdClaimType);
|
||||||
|
var startMediaId = identity.FindFirstValue(Constants.Security.StartMediaNodeIdClaimType);
|
||||||
|
|
||||||
|
var culture = identity.FindFirstValue(ClaimTypes.Locality);
|
||||||
|
var id = identity.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
var realName = identity.FindFirstValue(ClaimTypes.GivenName);
|
||||||
|
|
||||||
|
if (username == null || startContentId == null || startMediaId == null
|
||||||
|
|| culture == null || id == null
|
||||||
|
|| realName == null || session == null)
|
||||||
|
throw new InvalidOperationException("Cannot create a " + typeof(UmbracoBackOfficeIdentity) + " from " + typeof(ClaimsIdentity) + " since there are missing required claims");
|
||||||
|
|
||||||
|
int startContentIdAsInt;
|
||||||
|
int startMediaIdAsInt;
|
||||||
|
if (int.TryParse(startContentId, out startContentIdAsInt) == false || int.TryParse(startMediaId, out startMediaIdAsInt) == false)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Cannot create a " + typeof(UmbracoBackOfficeIdentity) + " from " + typeof(ClaimsIdentity) + " since the data is not formatted correctly");
|
||||||
|
}
|
||||||
|
|
||||||
|
var roles = identity.FindAll(x => x.Type == DefaultRoleClaimType).Select(role => role.Value).ToList();
|
||||||
|
var allowedApps = identity.FindAll(x => x.Type == Constants.Security.AllowedApplicationsClaimType).Select(app => app.Value).ToList();
|
||||||
|
|
||||||
|
var userData = new UserData(session)
|
||||||
|
{
|
||||||
|
SessionId = session,
|
||||||
|
AllowedApplications = allowedApps.ToArray(),
|
||||||
|
Culture = culture,
|
||||||
|
Id = id,
|
||||||
|
Roles = roles.ToArray(),
|
||||||
|
Username = username,
|
||||||
|
RealName = realName,
|
||||||
|
StartContentNode = startContentIdAsInt,
|
||||||
|
StartMediaNode = startMediaIdAsInt
|
||||||
|
};
|
||||||
|
|
||||||
|
return new UmbracoBackOfficeIdentity(identity, userData);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a back office identity based on user data
|
/// Create a back office identity based on user data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -44,9 +99,15 @@ namespace Umbraco.Core.Security
|
|||||||
if (claimsIdentity == null) throw new ArgumentNullException("claimsIdentity");
|
if (claimsIdentity == null) throw new ArgumentNullException("claimsIdentity");
|
||||||
if (userdata == null) throw new ArgumentNullException("userdata");
|
if (userdata == null) throw new ArgumentNullException("userdata");
|
||||||
|
|
||||||
|
if (claimsIdentity is FormsIdentity)
|
||||||
|
{
|
||||||
|
//since it's a forms auth ticket, it is from a cookie so add that claim
|
||||||
|
AddClaim(new Claim(ClaimTypes.CookiePath, "/", ClaimValueTypes.String, Issuer, Issuer, this));
|
||||||
|
}
|
||||||
|
|
||||||
_currentIssuer = claimsIdentity.AuthenticationType;
|
_currentIssuer = claimsIdentity.AuthenticationType;
|
||||||
UserData = userdata;
|
UserData = userdata;
|
||||||
AddClaims(claimsIdentity);
|
AddExistingClaims(claimsIdentity);
|
||||||
Actor = claimsIdentity;
|
Actor = claimsIdentity;
|
||||||
AddUserDataClaims();
|
AddUserDataClaims();
|
||||||
}
|
}
|
||||||
@@ -58,6 +119,9 @@ namespace Umbraco.Core.Security
|
|||||||
public UmbracoBackOfficeIdentity(FormsAuthenticationTicket ticket)
|
public UmbracoBackOfficeIdentity(FormsAuthenticationTicket ticket)
|
||||||
: base(ticket)
|
: base(ticket)
|
||||||
{
|
{
|
||||||
|
//since it's a forms auth ticket, it is from a cookie so add that claim
|
||||||
|
AddClaim(new Claim(ClaimTypes.CookiePath, "/", ClaimValueTypes.String, Issuer, Issuer, this));
|
||||||
|
|
||||||
UserData = JsonConvert.DeserializeObject<UserData>(ticket.UserData);
|
UserData = JsonConvert.DeserializeObject<UserData>(ticket.UserData);
|
||||||
AddUserDataClaims();
|
AddUserDataClaims();
|
||||||
}
|
}
|
||||||
@@ -72,7 +136,7 @@ namespace Umbraco.Core.Security
|
|||||||
if (identity.Actor != null)
|
if (identity.Actor != null)
|
||||||
{
|
{
|
||||||
_currentIssuer = identity.AuthenticationType;
|
_currentIssuer = identity.AuthenticationType;
|
||||||
AddClaims(identity);
|
AddExistingClaims(identity);
|
||||||
Actor = identity.Clone();
|
Actor = identity.Clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,43 +147,98 @@ namespace Umbraco.Core.Security
|
|||||||
public const string Issuer = "UmbracoBackOffice";
|
public const string Issuer = "UmbracoBackOffice";
|
||||||
private readonly string _currentIssuer = Issuer;
|
private readonly string _currentIssuer = Issuer;
|
||||||
|
|
||||||
private void AddClaims(ClaimsIdentity claimsIdentity)
|
/// <summary>
|
||||||
|
/// Used during ctor to add existing claims from an existing ClaimsIdentity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="claimsIdentity"></param>
|
||||||
|
private void AddExistingClaims(ClaimsIdentity claimsIdentity)
|
||||||
{
|
{
|
||||||
foreach (var claim in claimsIdentity.Claims)
|
foreach (var claim in claimsIdentity.Claims)
|
||||||
{
|
{
|
||||||
|
//In one special case we will replace a claim if it exists already and that is the
|
||||||
|
// Forms auth claim for name which automatically gets added
|
||||||
|
TryRemoveClaim(FindFirst(x => x.Type == claim.Type && x.Issuer == "Forms"));
|
||||||
|
|
||||||
AddClaim(claim);
|
AddClaim(claim);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the required claim types for a back office identity
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This does not incude the role claim type or allowed apps type since that is a collection and in theory could be empty
|
||||||
|
/// </remarks>
|
||||||
|
public static IEnumerable<string> RequiredBackOfficeIdentityClaimTypes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
ClaimTypes.NameIdentifier, //id
|
||||||
|
ClaimTypes.Name, //username
|
||||||
|
ClaimTypes.GivenName,
|
||||||
|
Constants.Security.StartContentNodeIdClaimType,
|
||||||
|
Constants.Security.StartMediaNodeIdClaimType,
|
||||||
|
ClaimTypes.Locality,
|
||||||
|
Constants.Security.SessionIdClaimType
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds claims based on the UserData data
|
/// Adds claims based on the UserData data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void AddUserDataClaims()
|
private void AddUserDataClaims()
|
||||||
{
|
{
|
||||||
AddClaims(new[]
|
//This is the id that 'identity' uses to check for the user id
|
||||||
|
if (HasClaim(x => x.Type == ClaimTypes.NameIdentifier) == false)
|
||||||
|
AddClaim(new Claim(ClaimTypes.NameIdentifier, UserData.Id.ToString(), ClaimValueTypes.Integer32, Issuer, Issuer, this));
|
||||||
|
|
||||||
|
if (HasClaim(x => x.Type == ClaimTypes.Name) == false)
|
||||||
|
AddClaim(new Claim(ClaimTypes.Name, UserData.Username, ClaimValueTypes.String, Issuer, Issuer, this));
|
||||||
|
|
||||||
|
if (HasClaim(x => x.Type == ClaimTypes.GivenName) == false)
|
||||||
|
AddClaim(new Claim(ClaimTypes.GivenName, UserData.RealName, ClaimValueTypes.String, Issuer, Issuer, this));
|
||||||
|
|
||||||
|
if (HasClaim(x => x.Type == Constants.Security.StartContentNodeIdClaimType) == false)
|
||||||
|
AddClaim(new Claim(Constants.Security.StartContentNodeIdClaimType, StartContentNode.ToInvariantString(), ClaimValueTypes.Integer32, Issuer, Issuer, this));
|
||||||
|
|
||||||
|
if (HasClaim(x => x.Type == Constants.Security.StartMediaNodeIdClaimType) == false)
|
||||||
|
AddClaim(new Claim(Constants.Security.StartMediaNodeIdClaimType, StartMediaNode.ToInvariantString(), ClaimValueTypes.Integer32, Issuer, Issuer, this));
|
||||||
|
|
||||||
|
if (HasClaim(x => x.Type == ClaimTypes.Locality) == false)
|
||||||
|
AddClaim(new Claim(ClaimTypes.Locality, Culture, ClaimValueTypes.String, Issuer, Issuer, this));
|
||||||
|
|
||||||
|
if (HasClaim(x => x.Type == Constants.Security.SessionIdClaimType) == false)
|
||||||
|
AddClaim(new Claim(Constants.Security.SessionIdClaimType, SessionId, ClaimValueTypes.String, Issuer, Issuer, this));
|
||||||
|
|
||||||
|
//Add each app as a separate claim
|
||||||
|
if (HasClaim(x => x.Type == Constants.Security.AllowedApplicationsClaimType) == false)
|
||||||
{
|
{
|
||||||
//This is the id that 'identity' uses to check for the user id
|
foreach (var application in AllowedApplications)
|
||||||
new Claim(ClaimTypes.NameIdentifier, Id.ToString(), null, Issuer, Issuer, this),
|
{
|
||||||
|
AddClaim(new Claim(Constants.Security.AllowedApplicationsClaimType, application, ClaimValueTypes.String, Issuer, Issuer, this));
|
||||||
new Claim(Constants.Security.StartContentNodeIdClaimType, StartContentNode.ToInvariantString(), null, Issuer, Issuer, this),
|
}
|
||||||
new Claim(Constants.Security.StartMediaNodeIdClaimType, StartMediaNode.ToInvariantString(), null, Issuer, Issuer, this),
|
|
||||||
new Claim(Constants.Security.AllowedApplicationsClaimType, string.Join(",", AllowedApplications), null, Issuer, Issuer, this),
|
|
||||||
|
|
||||||
//TODO: Similar one created by the ClaimsIdentityFactory<TUser, TKey> not sure we need this
|
|
||||||
new Claim(Constants.Security.CultureClaimType, Culture, null, Issuer, Issuer, this)
|
|
||||||
|
|
||||||
//TODO: Role claims are added by the default ClaimsIdentityFactory<TUser, TKey> based on the result from
|
|
||||||
// the user manager manager.GetRolesAsync method so not sure if we can do that there or needs to be done here
|
|
||||||
// and each role should be a different claim, not a single string
|
|
||||||
|
|
||||||
//new Claim(ClaimTypes.Role, string.Join(",", Roles), null, Issuer, Issuer, this)
|
|
||||||
});
|
|
||||||
|
|
||||||
//TODO: Find out why sessionid is null - this depends on how the identity is created!
|
|
||||||
if (SessionId.IsNullOrWhiteSpace() == false)
|
|
||||||
{
|
|
||||||
AddClaim(new Claim(Constants.Security.SessionIdClaimType, SessionId, null, Issuer, Issuer, this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Claims are added by the ClaimsIdentityFactory because our UserStore supports roles, however this identity might
|
||||||
|
// not be made with that factory if it was created with a FormsAuthentication ticket so perform the check
|
||||||
|
if (HasClaim(x => x.Type == DefaultRoleClaimType) == false)
|
||||||
|
{
|
||||||
|
//manually add them based on the UserData
|
||||||
|
foreach (var roleName in UserData.Roles)
|
||||||
|
{
|
||||||
|
AddClaim(new Claim(RoleClaimType, roleName, ClaimValueTypes.String, Issuer, Issuer, this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////TODO: Find out why sessionid is null - this depends on how the identity is created!
|
||||||
|
//// in this case generate one?
|
||||||
|
//if (SessionId.IsNullOrWhiteSpace() == false)
|
||||||
|
//{
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,6 +280,11 @@ namespace Umbraco.Core.Security
|
|||||||
get { return UserData.RealName; }
|
get { return UserData.RealName; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Username
|
||||||
|
{
|
||||||
|
get { return UserData.Username; }
|
||||||
|
}
|
||||||
|
|
||||||
public string Culture
|
public string Culture
|
||||||
{
|
{
|
||||||
get { return UserData.Culture; }
|
get { return UserData.Culture; }
|
||||||
|
|||||||
@@ -345,6 +345,8 @@
|
|||||||
<Compile Include="HideFromTypeFinderAttribute.cs" />
|
<Compile Include="HideFromTypeFinderAttribute.cs" />
|
||||||
<Compile Include="IApplicationEventHandler.cs" />
|
<Compile Include="IApplicationEventHandler.cs" />
|
||||||
<Compile Include="IDisposeOnRequestEnd.cs" />
|
<Compile Include="IDisposeOnRequestEnd.cs" />
|
||||||
|
<Compile Include="Logging\OwinLogger.cs" />
|
||||||
|
<Compile Include="Logging\OwinLoggerFactory.cs" />
|
||||||
<Compile Include="Manifest\GridEditorConverter.cs" />
|
<Compile Include="Manifest\GridEditorConverter.cs" />
|
||||||
<Compile Include="Models\AuditItem.cs" />
|
<Compile Include="Models\AuditItem.cs" />
|
||||||
<Compile Include="Models\AuditType.cs" />
|
<Compile Include="Models\AuditType.cs" />
|
||||||
|
|||||||
189
src/Umbraco.Tests/Security/UmbracoBackOfficeIdentityTests.cs
Normal file
189
src/Umbraco.Tests/Security/UmbracoBackOfficeIdentityTests.cs
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web.Security;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Umbraco.Core;
|
||||||
|
using Umbraco.Core.Security;
|
||||||
|
|
||||||
|
namespace Umbraco.Tests.Security
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class UmbracoBackOfficeIdentityTests
|
||||||
|
{
|
||||||
|
|
||||||
|
public const string TestIssuer = "TestIssuer";
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Create_From_Claims_Identity()
|
||||||
|
{
|
||||||
|
var sessionId = Guid.NewGuid().ToString();
|
||||||
|
var claimsIdentity = new ClaimsIdentity(new[]
|
||||||
|
{
|
||||||
|
//This is the id that 'identity' uses to check for the user id
|
||||||
|
new Claim(ClaimTypes.NameIdentifier, "1234", ClaimValueTypes.Integer32, TestIssuer, TestIssuer),
|
||||||
|
//This is the id that 'identity' uses to check for the username
|
||||||
|
new Claim(ClaimTypes.Name, "testing", ClaimValueTypes.String, TestIssuer, TestIssuer),
|
||||||
|
new Claim(ClaimTypes.GivenName, "hello world", ClaimValueTypes.String, TestIssuer, TestIssuer),
|
||||||
|
new Claim(Constants.Security.StartContentNodeIdClaimType, "-1", ClaimValueTypes.Integer32, TestIssuer, TestIssuer),
|
||||||
|
new Claim(Constants.Security.StartMediaNodeIdClaimType, "5543", ClaimValueTypes.Integer32, TestIssuer, TestIssuer),
|
||||||
|
new Claim(Constants.Security.AllowedApplicationsClaimType, "content", ClaimValueTypes.String, TestIssuer, TestIssuer),
|
||||||
|
new Claim(Constants.Security.AllowedApplicationsClaimType, "media", ClaimValueTypes.String, TestIssuer, TestIssuer),
|
||||||
|
new Claim(ClaimTypes.Locality, "en-us", ClaimValueTypes.String, TestIssuer, TestIssuer),
|
||||||
|
new Claim(Constants.Security.SessionIdClaimType, sessionId, Constants.Security.SessionIdClaimType, TestIssuer, TestIssuer),
|
||||||
|
new Claim(ClaimsIdentity.DefaultRoleClaimType, "admin", ClaimValueTypes.String, TestIssuer, TestIssuer),
|
||||||
|
});
|
||||||
|
|
||||||
|
var backofficeIdentity = UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity);
|
||||||
|
|
||||||
|
Assert.AreEqual("1234", backofficeIdentity.Id);
|
||||||
|
Assert.AreEqual(sessionId, backofficeIdentity.SessionId);
|
||||||
|
Assert.AreEqual("testing", backofficeIdentity.Username);
|
||||||
|
Assert.AreEqual("hello world", backofficeIdentity.RealName);
|
||||||
|
Assert.AreEqual(-1, backofficeIdentity.StartContentNode);
|
||||||
|
Assert.AreEqual(5543, backofficeIdentity.StartMediaNode);
|
||||||
|
Assert.IsTrue(new[] {"content", "media"}.SequenceEqual(backofficeIdentity.AllowedApplications));
|
||||||
|
Assert.AreEqual("en-us", backofficeIdentity.Culture);
|
||||||
|
Assert.IsTrue(new[] { "admin" }.SequenceEqual(backofficeIdentity.Roles));
|
||||||
|
|
||||||
|
Assert.AreEqual(10, backofficeIdentity.Claims.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Create_From_Claims_Identity_Missing_Required_Claim()
|
||||||
|
{
|
||||||
|
var claimsIdentity = new ClaimsIdentity(new[]
|
||||||
|
{
|
||||||
|
new Claim(ClaimTypes.NameIdentifier, "1234", ClaimValueTypes.Integer32, TestIssuer, TestIssuer),
|
||||||
|
new Claim(ClaimTypes.Name, "testing", ClaimValueTypes.String, TestIssuer, TestIssuer),
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.Throws<InvalidOperationException>(() => UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Create_From_Claims_Identity_Required_Claim_Null()
|
||||||
|
{
|
||||||
|
var sessionId = Guid.NewGuid().ToString();
|
||||||
|
var claimsIdentity = new ClaimsIdentity(new[]
|
||||||
|
{
|
||||||
|
//null or empty
|
||||||
|
new Claim(ClaimTypes.NameIdentifier, "", ClaimValueTypes.Integer32, TestIssuer, TestIssuer),
|
||||||
|
new Claim(ClaimTypes.Name, "testing", ClaimValueTypes.String, TestIssuer, TestIssuer),
|
||||||
|
new Claim(ClaimTypes.GivenName, "hello world", ClaimValueTypes.String, TestIssuer, TestIssuer),
|
||||||
|
new Claim(Constants.Security.StartContentNodeIdClaimType, "-1", ClaimValueTypes.Integer32, TestIssuer, TestIssuer),
|
||||||
|
new Claim(Constants.Security.StartMediaNodeIdClaimType, "5543", ClaimValueTypes.Integer32, TestIssuer, TestIssuer),
|
||||||
|
new Claim(Constants.Security.AllowedApplicationsClaimType, "content", ClaimValueTypes.String, TestIssuer, TestIssuer),
|
||||||
|
new Claim(Constants.Security.AllowedApplicationsClaimType, "media", ClaimValueTypes.String, TestIssuer, TestIssuer),
|
||||||
|
new Claim(ClaimTypes.Locality, "en-us", ClaimValueTypes.String, TestIssuer, TestIssuer),
|
||||||
|
new Claim(Constants.Security.SessionIdClaimType, sessionId, Constants.Security.SessionIdClaimType, TestIssuer, TestIssuer),
|
||||||
|
new Claim(ClaimsIdentity.DefaultRoleClaimType, "admin", ClaimValueTypes.String, TestIssuer, TestIssuer),
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.Throws<InvalidOperationException>(() => UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Create_With_User_Data()
|
||||||
|
{
|
||||||
|
var sessionId = Guid.NewGuid().ToString();
|
||||||
|
var userData = new UserData(sessionId)
|
||||||
|
{
|
||||||
|
AllowedApplications = new[] {"content", "media"},
|
||||||
|
Culture = "en-us",
|
||||||
|
Id = 1234,
|
||||||
|
RealName = "hello world",
|
||||||
|
Roles = new[] {"admin"},
|
||||||
|
StartContentNode = -1,
|
||||||
|
StartMediaNode = 654,
|
||||||
|
Username = "testing"
|
||||||
|
};
|
||||||
|
|
||||||
|
var identity = new UmbracoBackOfficeIdentity(userData);
|
||||||
|
|
||||||
|
Assert.AreEqual(10, identity.Claims.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Create_With_Claims_And_User_Data()
|
||||||
|
{
|
||||||
|
var sessionId = Guid.NewGuid().ToString();
|
||||||
|
var userData = new UserData(sessionId)
|
||||||
|
{
|
||||||
|
AllowedApplications = new[] { "content", "media" },
|
||||||
|
Culture = "en-us",
|
||||||
|
Id = 1234,
|
||||||
|
RealName = "hello world",
|
||||||
|
Roles = new[] { "admin" },
|
||||||
|
StartContentNode = -1,
|
||||||
|
StartMediaNode = 654,
|
||||||
|
Username = "testing"
|
||||||
|
};
|
||||||
|
|
||||||
|
var claimsIdentity = new ClaimsIdentity(new[]
|
||||||
|
{
|
||||||
|
new Claim("TestClaim1", "test", ClaimValueTypes.Integer32, TestIssuer, TestIssuer),
|
||||||
|
new Claim("TestClaim1", "test", ClaimValueTypes.Integer32, TestIssuer, TestIssuer)
|
||||||
|
});
|
||||||
|
|
||||||
|
var backofficeIdentity = new UmbracoBackOfficeIdentity(claimsIdentity, userData);
|
||||||
|
|
||||||
|
Assert.AreEqual(12, backofficeIdentity.Claims.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Create_With_Forms_Ticket()
|
||||||
|
{
|
||||||
|
var sessionId = Guid.NewGuid().ToString();
|
||||||
|
var userData = new UserData(sessionId)
|
||||||
|
{
|
||||||
|
AllowedApplications = new[] { "content", "media" },
|
||||||
|
Culture = "en-us",
|
||||||
|
Id = 1234,
|
||||||
|
RealName = "hello world",
|
||||||
|
Roles = new[] { "admin" },
|
||||||
|
StartContentNode = -1,
|
||||||
|
StartMediaNode = 654,
|
||||||
|
Username = "testing"
|
||||||
|
};
|
||||||
|
|
||||||
|
var ticket = new FormsAuthenticationTicket(1, userData.Username, DateTime.Now, DateTime.Now.AddDays(1), true,
|
||||||
|
JsonConvert.SerializeObject(userData));
|
||||||
|
|
||||||
|
var identity = new UmbracoBackOfficeIdentity(ticket);
|
||||||
|
|
||||||
|
Assert.AreEqual(11, identity.Claims.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Clone()
|
||||||
|
{
|
||||||
|
var sessionId = Guid.NewGuid().ToString();
|
||||||
|
var userData = new UserData(sessionId)
|
||||||
|
{
|
||||||
|
AllowedApplications = new[] { "content", "media" },
|
||||||
|
Culture = "en-us",
|
||||||
|
Id = 1234,
|
||||||
|
RealName = "hello world",
|
||||||
|
Roles = new[] { "admin" },
|
||||||
|
StartContentNode = -1,
|
||||||
|
StartMediaNode = 654,
|
||||||
|
Username = "testing"
|
||||||
|
};
|
||||||
|
|
||||||
|
var ticket = new FormsAuthenticationTicket(1, userData.Username, DateTime.Now, DateTime.Now.AddDays(1), true,
|
||||||
|
JsonConvert.SerializeObject(userData));
|
||||||
|
|
||||||
|
var identity = new UmbracoBackOfficeIdentity(ticket);
|
||||||
|
|
||||||
|
var cloned = identity.Clone();
|
||||||
|
|
||||||
|
Assert.AreEqual(11, cloned.Claims.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -181,6 +181,7 @@
|
|||||||
<Compile Include="Persistence\Repositories\TaskTypeRepositoryTest.cs" />
|
<Compile Include="Persistence\Repositories\TaskTypeRepositoryTest.cs" />
|
||||||
<Compile Include="Resolvers\ResolverBaseTest.cs" />
|
<Compile Include="Resolvers\ResolverBaseTest.cs" />
|
||||||
<Compile Include="Routing\UrlRoutingTestBase.cs" />
|
<Compile Include="Routing\UrlRoutingTestBase.cs" />
|
||||||
|
<Compile Include="Security\UmbracoBackOfficeIdentityTests.cs" />
|
||||||
<Compile Include="StringNewlineExtensions.cs" />
|
<Compile Include="StringNewlineExtensions.cs" />
|
||||||
<Compile Include="Strings\StylesheetHelperTests.cs" />
|
<Compile Include="Strings\StylesheetHelperTests.cs" />
|
||||||
<Compile Include="Strings\StringValidationTests.cs" />
|
<Compile Include="Strings\StringValidationTests.cs" />
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ namespace Umbraco.Web.Editors
|
|||||||
{
|
{
|
||||||
var cultureInfo = culture == null
|
var cultureInfo = culture == null
|
||||||
//if the user is logged in, get their culture, otherwise default to 'en'
|
//if the user is logged in, get their culture, otherwise default to 'en'
|
||||||
? User.Identity.IsAuthenticated && User.Identity is UmbracoBackOfficeIdentity
|
? User.Identity.IsAuthenticated
|
||||||
? Security.CurrentUser.GetUserCulture(Services.TextService)
|
? Security.CurrentUser.GetUserCulture(Services.TextService)
|
||||||
: CultureInfo.GetCultureInfo("en")
|
: CultureInfo.GetCultureInfo("en")
|
||||||
: CultureInfo.GetCultureInfo(culture);
|
: CultureInfo.GetCultureInfo(culture);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Microsoft.AspNet.Identity;
|
|||||||
using Microsoft.AspNet.Identity.Owin;
|
using Microsoft.AspNet.Identity.Owin;
|
||||||
using Microsoft.Owin;
|
using Microsoft.Owin;
|
||||||
using Microsoft.Owin.Extensions;
|
using Microsoft.Owin.Extensions;
|
||||||
|
using Microsoft.Owin.Logging;
|
||||||
using Microsoft.Owin.Security;
|
using Microsoft.Owin.Security;
|
||||||
using Microsoft.Owin.Security.Cookies;
|
using Microsoft.Owin.Security.Cookies;
|
||||||
using Owin;
|
using Owin;
|
||||||
@@ -20,6 +21,15 @@ namespace Umbraco.Web.Security.Identity
|
|||||||
{
|
{
|
||||||
public static class AppBuilderExtensions
|
public static class AppBuilderExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the OWIN logger to use Umbraco's logging system
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="app"></param>
|
||||||
|
public static void SetUmbracoLoggerFactory(this IAppBuilder app)
|
||||||
|
{
|
||||||
|
app.SetLoggerFactory(new OwinLoggerFactory());
|
||||||
|
}
|
||||||
|
|
||||||
#region Backoffice
|
#region Backoffice
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -112,7 +122,7 @@ namespace Umbraco.Web.Security.Identity
|
|||||||
GlobalSettings.UseSSL)
|
GlobalSettings.UseSSL)
|
||||||
{
|
{
|
||||||
Provider = new CookieAuthenticationProvider
|
Provider = new CookieAuthenticationProvider
|
||||||
{
|
{
|
||||||
// Enables the application to validate the security stamp when the user
|
// Enables the application to validate the security stamp when the user
|
||||||
// logs in. This is a security feature which is used when you
|
// logs in. This is a security feature which is used when you
|
||||||
// change a password or add an external login to your account.
|
// change a password or add an external login to your account.
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Identity.Owin;
|
||||||
|
using Microsoft.Owin.Security.OAuth;
|
||||||
|
using Umbraco.Core.Security;
|
||||||
|
|
||||||
|
namespace Umbraco.Web.Security.Identity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A simple OAuth server provider to verify back office users
|
||||||
|
/// </summary>
|
||||||
|
public class BackOfficeAuthorizationServerProvider : OAuthAuthorizationServerProvider
|
||||||
|
{
|
||||||
|
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
|
||||||
|
{
|
||||||
|
var userManager = context.OwinContext.GetUserManager<BackOfficeUserManager>();
|
||||||
|
|
||||||
|
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
|
||||||
|
|
||||||
|
var user = await userManager.FindAsync(context.UserName, context.Password);
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
context.SetError("invalid_grant", "The user name or password is incorrect.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var identity = await userManager.ClaimsIdentityFactory.CreateAsync(userManager, user, context.Options.AuthenticationType);
|
||||||
|
|
||||||
|
context.Validated(identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -309,6 +309,7 @@
|
|||||||
<Compile Include="ApplicationContextExtensions.cs" />
|
<Compile Include="ApplicationContextExtensions.cs" />
|
||||||
<Compile Include="AreaRegistrationContextExtensions.cs" />
|
<Compile Include="AreaRegistrationContextExtensions.cs" />
|
||||||
<Compile Include="HtmlHelperBackOfficeExtensions.cs" />
|
<Compile Include="HtmlHelperBackOfficeExtensions.cs" />
|
||||||
|
<Compile Include="Security\Identity\BackOfficeAuthorizationServerProvider.cs" />
|
||||||
<Compile Include="UmbracoDefaultOwinStartup.cs" />
|
<Compile Include="UmbracoDefaultOwinStartup.cs" />
|
||||||
<Compile Include="IUmbracoContextAccessor.cs" />
|
<Compile Include="IUmbracoContextAccessor.cs" />
|
||||||
<Compile Include="Models\ContentEditing\Relation.cs" />
|
<Compile Include="Models\ContentEditing\Relation.cs" />
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using Microsoft.Owin;
|
using Microsoft.Owin;
|
||||||
|
using Microsoft.Owin.Logging;
|
||||||
using Owin;
|
using Owin;
|
||||||
using Umbraco.Core;
|
using Umbraco.Core;
|
||||||
|
using Umbraco.Core.Logging;
|
||||||
using Umbraco.Core.Security;
|
using Umbraco.Core.Security;
|
||||||
using Umbraco.Web;
|
using Umbraco.Web;
|
||||||
using Umbraco.Web.Security.Identity;
|
using Umbraco.Web.Security.Identity;
|
||||||
@@ -19,6 +21,8 @@ namespace Umbraco.Web
|
|||||||
{
|
{
|
||||||
public virtual void Configuration(IAppBuilder app)
|
public virtual void Configuration(IAppBuilder app)
|
||||||
{
|
{
|
||||||
|
app.SetUmbracoLoggerFactory();
|
||||||
|
|
||||||
//Configure the Identity user manager for use with Umbraco Back office
|
//Configure the Identity user manager for use with Umbraco Back office
|
||||||
// (EXPERT: an overload accepts a custom BackOfficeUserStore implementation)
|
// (EXPERT: an overload accepts a custom BackOfficeUserStore implementation)
|
||||||
app.ConfigureUserManagerForUmbracoBackOffice(
|
app.ConfigureUserManagerForUmbracoBackOffice(
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Web.Http;
|
||||||
using System.Web.Http.Filters;
|
using System.Web.Http.Filters;
|
||||||
|
|
||||||
namespace Umbraco.Web.WebApi.Filters
|
namespace Umbraco.Web.WebApi.Filters
|
||||||
@@ -10,13 +12,23 @@ namespace Umbraco.Web.WebApi.Filters
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Code derived from http://ericpanorel.net/2013/07/28/spa-authentication-and-csrf-mvc4-antiforgery-implementation/
|
/// Code derived from http://ericpanorel.net/2013/07/28/spa-authentication-and-csrf-mvc4-antiforgery-implementation/
|
||||||
///
|
///
|
||||||
/// TODO: If/when we enable custom authorization (OAuth, or whatever) we'll need to detect that and disable this filter since with custom auth that
|
/// If the authentication type is cookie based, then this filter will execute, otherwise it will be disabled
|
||||||
/// doesn't come from the same website (cookie), this will always fail.
|
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public sealed class ValidateAngularAntiForgeryTokenAttribute : ActionFilterAttribute
|
public sealed class ValidateAngularAntiForgeryTokenAttribute : ActionFilterAttribute
|
||||||
{
|
{
|
||||||
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
|
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
|
||||||
{
|
{
|
||||||
|
var userIdentity = ((ApiController) actionContext.ControllerContext.Controller).User.Identity as ClaimsIdentity;
|
||||||
|
if (userIdentity != null)
|
||||||
|
{
|
||||||
|
//if there is not CookiePath claim, then exist
|
||||||
|
if (userIdentity.HasClaim(x => x.Type == ClaimTypes.CookiePath) == false)
|
||||||
|
{
|
||||||
|
base.OnActionExecuting(actionContext);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string failedReason;
|
string failedReason;
|
||||||
if (AngularAntiForgeryHelper.ValidateHeaders(actionContext.Request.Headers, out failedReason) == false)
|
if (AngularAntiForgeryHelper.ValidateHeaders(actionContext.Request.Headers, out failedReason) == false)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user