Updates UmbracoBackOfficeIdentity to add claims and adds a new ctor so people can create an identity manually - this is really the key, by doing this we'd already be able to have 3rd party authentication happening. Ensures our custom secure data format persists the user data
This commit is contained in:
@@ -18,5 +18,17 @@
|
||||
public const string AuthCookieName = "UMB_UCONTEXT";
|
||||
|
||||
}
|
||||
|
||||
public static class Security
|
||||
{
|
||||
|
||||
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 AllowedApplicationsClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/allowedapps";
|
||||
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";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +1,137 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A custom user identity for the Umbraco backoffice
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All values are lazy loaded for performance reasons as the constructor is called for every single request
|
||||
/// This inherits from FormsIdentity for backwards compatibility reasons since we still support the forms auth cookie, in v8 we can
|
||||
/// change over to 'pure' asp.net identity and just inherit from ClaimsIdentity.
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
public class UmbracoBackOfficeIdentity : FormsIdentity
|
||||
{
|
||||
public UmbracoBackOfficeIdentity(FormsAuthenticationTicket ticket)
|
||||
/// <summary>
|
||||
/// Create a back office identity based on user data
|
||||
/// </summary>
|
||||
/// <param name="userdata"></param>
|
||||
public UmbracoBackOfficeIdentity(UserData userdata)
|
||||
//This just creates a temp/fake ticket
|
||||
: base(new FormsAuthenticationTicket(userdata.Username, true, 10))
|
||||
{
|
||||
UserData = userdata;
|
||||
AddClaims();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new identity from a forms auth ticket
|
||||
/// </summary>
|
||||
/// <param name="ticket"></param>
|
||||
public UmbracoBackOfficeIdentity(FormsAuthenticationTicket ticket)
|
||||
: base(ticket)
|
||||
{
|
||||
UserData = ticket.UserData;
|
||||
EnsureDeserialized();
|
||||
UserData = JsonConvert.DeserializeObject<UserData>(ticket.UserData);
|
||||
AddClaims();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for cloning
|
||||
/// </summary>
|
||||
/// <param name="identity"></param>
|
||||
private UmbracoBackOfficeIdentity(UmbracoBackOfficeIdentity identity)
|
||||
: base(identity)
|
||||
{
|
||||
UserData = identity.UserData;
|
||||
AddClaims();
|
||||
}
|
||||
|
||||
public static string Issuer = "UmbracoBackOffice";
|
||||
|
||||
//TODO: Another option is to create a ClaimsIdentityFactory when everything is wired up... optional though i think
|
||||
private void AddClaims()
|
||||
{
|
||||
AddClaims(new[]
|
||||
{
|
||||
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),
|
||||
new Claim(Constants.Security.UserIdClaimType, Id.ToString(), null, Issuer, Issuer, this),
|
||||
new Claim(Constants.Security.CultureClaimType, Culture, null, Issuer, Issuer, this),
|
||||
new Claim(Constants.Security.SessionIdClaimType, SessionId, null, Issuer, Issuer, this),
|
||||
new Claim(ClaimTypes.Role, string.Join(",", Roles), null, Issuer, Issuer, this)
|
||||
});
|
||||
}
|
||||
|
||||
protected internal UserData UserData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of authenticated identity.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The type of authenticated identity. This property always returns "UmbracoBackOffice".
|
||||
/// </returns>
|
||||
public override string AuthenticationType
|
||||
{
|
||||
get { return Issuer; }
|
||||
}
|
||||
|
||||
protected readonly string UserData;
|
||||
internal UserData DeserializedData;
|
||||
|
||||
public int StartContentNode
|
||||
{
|
||||
get
|
||||
{
|
||||
return DeserializedData.StartContentNode;
|
||||
}
|
||||
get { return UserData.StartContentNode; }
|
||||
}
|
||||
|
||||
public int StartMediaNode
|
||||
{
|
||||
get { return DeserializedData.StartMediaNode; }
|
||||
get { return UserData.StartMediaNode; }
|
||||
}
|
||||
|
||||
public string[] AllowedApplications
|
||||
{
|
||||
get { return DeserializedData.AllowedApplications; }
|
||||
get { return UserData.AllowedApplications; }
|
||||
}
|
||||
|
||||
|
||||
public object Id
|
||||
{
|
||||
get { return DeserializedData.Id; }
|
||||
get { return UserData.Id; }
|
||||
}
|
||||
|
||||
public string RealName
|
||||
{
|
||||
get { return DeserializedData.RealName; }
|
||||
get { return UserData.RealName; }
|
||||
}
|
||||
|
||||
public string Culture
|
||||
{
|
||||
get { return DeserializedData.Culture; }
|
||||
get { return UserData.Culture; }
|
||||
}
|
||||
|
||||
public string SessionId
|
||||
{
|
||||
get { return DeserializedData.SessionId; }
|
||||
get { return UserData.SessionId; }
|
||||
}
|
||||
|
||||
//public int SessionTimeout
|
||||
//{
|
||||
// get
|
||||
// {
|
||||
// EnsureDeserialized();
|
||||
// return DeserializedData.SessionTimeout;
|
||||
// }
|
||||
//}
|
||||
|
||||
public string[] Roles
|
||||
{
|
||||
get { return DeserializedData.Roles; }
|
||||
get { return UserData.Roles; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will ensure we only deserialize once
|
||||
/// Gets a copy of the current <see cref="T:UmbracoBackOfficeIdentity"/> instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For performance reasons, we'll also check if there's an http context available,
|
||||
/// if so, we'll chuck our instance in there so that we only deserialize once per request.
|
||||
/// </remarks>
|
||||
protected void EnsureDeserialized()
|
||||
/// <returns>
|
||||
/// A copy of the current <see cref="T:UmbracoBackOfficeIdentity"/> instance.
|
||||
/// </returns>
|
||||
public override ClaimsIdentity Clone()
|
||||
{
|
||||
if (DeserializedData != null)
|
||||
return;
|
||||
|
||||
if (HttpContext.Current != null)
|
||||
{
|
||||
//check if we've already done this in this request
|
||||
var data = HttpContext.Current.Items[typeof(UmbracoBackOfficeIdentity)] as UserData;
|
||||
if (data != null)
|
||||
{
|
||||
DeserializedData = data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(UserData))
|
||||
{
|
||||
throw new NullReferenceException("The " + typeof(UserData) + " found in the ticket cannot be empty");
|
||||
}
|
||||
DeserializedData = JsonConvert.DeserializeObject<UserData>(UserData);
|
||||
|
||||
if (HttpContext.Current != null)
|
||||
{
|
||||
HttpContext.Current.Items[typeof (UmbracoBackOfficeIdentity)] = DeserializedData;
|
||||
}
|
||||
return new UmbracoBackOfficeIdentity(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -54,9 +54,25 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\log4net-mediumtrust.2.0.0\lib\log4net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNet.Identity.Core">
|
||||
<HintPath>..\packages\Microsoft.AspNet.Identity.Core.2.1.0\lib\net45\Microsoft.AspNet.Identity.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNet.Identity.Owin">
|
||||
<HintPath>..\packages\Microsoft.AspNet.Identity.Owin.2.1.0\lib\net45\Microsoft.AspNet.Identity.Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin">
|
||||
<HintPath>..\packages\Microsoft.Owin.3.0.0\lib\net45\Microsoft.Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Security">
|
||||
<HintPath>..\packages\Microsoft.Owin.Security.2.1.0\lib\net45\Microsoft.Owin.Security.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Security.Cookies">
|
||||
<HintPath>..\packages\Microsoft.Owin.Security.Cookies.2.1.0\lib\net45\Microsoft.Owin.Security.Cookies.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Security.OAuth">
|
||||
<HintPath>..\packages\Microsoft.Owin.Security.OAuth.2.1.0\lib\net45\Microsoft.Owin.Security.OAuth.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
|
||||
@@ -326,6 +342,10 @@
|
||||
<Compile Include="IDisposeOnRequestEnd.cs" />
|
||||
<Compile Include="Models\AuditItem.cs" />
|
||||
<Compile Include="Models\AuditType.cs" />
|
||||
<Compile Include="Models\Identity\IdentityUser.cs" />
|
||||
<Compile Include="Models\Identity\IdentityUserClaim.cs" />
|
||||
<Compile Include="Models\Identity\IdentityUserLogin.cs" />
|
||||
<Compile Include="Models\Identity\IdentityUserRole.cs" />
|
||||
<Compile Include="Models\PublicAccessEntry.cs" />
|
||||
<Compile Include="Models\PublicAccessRule.cs" />
|
||||
<Compile Include="Models\Rdbms\AccessDto.cs" />
|
||||
@@ -343,6 +363,7 @@
|
||||
<Compile Include="Media\Exif\ExifTagFactory.cs" />
|
||||
<Compile Include="Models\Rdbms\AccessRuleDto.cs" />
|
||||
<Compile Include="Models\Rdbms\DocumentPublishedReadOnlyDto.cs" />
|
||||
<Compile Include="Models\Rdbms\ExternalLoginDto.cs" />
|
||||
<Compile Include="Models\UmbracoDomain.cs" />
|
||||
<Compile Include="Models\DoNotCloneAttribute.cs" />
|
||||
<Compile Include="Models\IDomain.cs" />
|
||||
@@ -387,6 +408,7 @@
|
||||
<Compile Include="Persistence\Repositories\RepositoryCacheOptions.cs" />
|
||||
<Compile Include="Persistence\Repositories\TaskRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\TaskTypeRepository.cs" />
|
||||
<Compile Include="Security\BackOfficeUserManager.cs" />
|
||||
<Compile Include="ServiceProviderExtensions.cs" />
|
||||
<Compile Include="IO\ResizedImage.cs" />
|
||||
<Compile Include="IO\UmbracoMediaFile.cs" />
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
|
||||
<package id="HtmlAgilityPack" version="1.4.6" targetFramework="net45" />
|
||||
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net40" />
|
||||
<package id="Microsoft.AspNet.Identity.Core" version="2.1.0" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.Identity.Owin" version="2.1.0" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net40" />
|
||||
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net40" />
|
||||
<package id="Microsoft.AspNet.WebApi.Client" version="4.0.30506.0" targetFramework="net45" />
|
||||
@@ -11,6 +13,9 @@
|
||||
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net45" />
|
||||
<package id="Microsoft.Net.Http" version="2.2.28" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin" version="3.0.0" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Security" version="2.1.0" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Security.Cookies" version="2.1.0" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Security.OAuth" version="2.1.0" targetFramework="net45" />
|
||||
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net40" />
|
||||
<package id="MiniProfiler" version="2.1.0" targetFramework="net45" />
|
||||
<package id="MySql.Data" version="6.6.5" targetFramework="net40" />
|
||||
|
||||
@@ -50,9 +50,11 @@ namespace Umbraco.Web.UI
|
||||
|
||||
//Ensure owin is configured for Umbraco back office authentication - this must
|
||||
// be configured AFTER the standard UseCookieConfiguration above.
|
||||
app.UseUmbracoBackAuthentication();
|
||||
app
|
||||
.UseUmbracoBackOfficeCookieAuthentication()
|
||||
.UseUmbracoBackOfficeExternalCookieAuthentication();
|
||||
|
||||
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
|
||||
//app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Microsoft.AspNet.Identity.Owin;
|
||||
@@ -49,7 +51,7 @@ namespace Umbraco.Web.Security.Identity
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
public static IAppBuilder UseUmbracoBackAuthentication(this IAppBuilder app)
|
||||
public static IAppBuilder UseUmbracoBackOfficeCookieAuthentication(this IAppBuilder app)
|
||||
{
|
||||
if (app == null) throw new ArgumentNullException("app");
|
||||
|
||||
@@ -60,21 +62,30 @@ namespace Umbraco.Web.Security.Identity
|
||||
GlobalSettings.UseSSL,
|
||||
GlobalSettings.Path)
|
||||
{
|
||||
//Provider = new CookieAuthenticationProvider
|
||||
//{
|
||||
// // Enables the application to validate the security stamp when the user
|
||||
// // logs in. This is a security feature which is used when you
|
||||
// // change a password or add an external login to your account.
|
||||
// OnValidateIdentity = SecurityStampValidator
|
||||
// .OnValidateIdentity<UmbracoMembersUserManager<UmbracoApplicationUser>, UmbracoApplicationUser, int>(
|
||||
// TimeSpan.FromMinutes(30),
|
||||
// (manager, user) => user.GenerateUserIdentityAsync(manager),
|
||||
// identity => identity.GetUserId<int>())
|
||||
//}
|
||||
Provider = new CookieAuthenticationProvider
|
||||
{
|
||||
//// Enables the application to validate the security stamp when the user
|
||||
//// logs in. This is a security feature which is used when you
|
||||
//// change a password or add an external login to your account.
|
||||
//OnValidateIdentity = SecurityStampValidator
|
||||
// .OnValidateIdentity<UmbracoMembersUserManager<UmbracoApplicationUser>, UmbracoApplicationUser, int>(
|
||||
// TimeSpan.FromMinutes(30),
|
||||
// (manager, user) => user.GenerateUserIdentityAsync(manager),
|
||||
// identity => identity.GetUserId<int>())
|
||||
}
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
public static IAppBuilder UseUmbracoBackOfficeExternalCookieAuthentication(this IAppBuilder app)
|
||||
{
|
||||
if (app == null) throw new ArgumentNullException("app");
|
||||
|
||||
app.UseExternalSignInCookie("UmbracoExternalCookie");
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Security.Claims;
|
||||
using System.Web.Security;
|
||||
using Microsoft.Owin.Security;
|
||||
using Newtonsoft.Json;
|
||||
@@ -12,16 +13,18 @@ namespace Umbraco.Web.Security.Identity
|
||||
internal class FormsAuthenticationSecureDataFormat : ISecureDataFormat<AuthenticationTicket>
|
||||
{
|
||||
private readonly int _loginTimeoutMinutes;
|
||||
private readonly string _cookiePath;
|
||||
|
||||
public FormsAuthenticationSecureDataFormat(int loginTimeoutMinutes)
|
||||
public FormsAuthenticationSecureDataFormat(int loginTimeoutMinutes, string cookiePath)
|
||||
{
|
||||
_loginTimeoutMinutes = loginTimeoutMinutes;
|
||||
_cookiePath = cookiePath;
|
||||
}
|
||||
|
||||
public string Protect(AuthenticationTicket data)
|
||||
{
|
||||
//TODO: Where to get the user data?
|
||||
//var userDataString = JsonConvert.SerializeObject(userdata);
|
||||
var backofficeIdentity = (UmbracoBackOfficeIdentity)data.Identity;
|
||||
var userDataString = JsonConvert.SerializeObject(backofficeIdentity.UserData);
|
||||
|
||||
var ticket = new FormsAuthenticationTicket(
|
||||
5,
|
||||
@@ -29,8 +32,8 @@ namespace Umbraco.Web.Security.Identity
|
||||
data.Properties.IssuedUtc.HasValue ? data.Properties.IssuedUtc.Value.LocalDateTime : DateTime.Now,
|
||||
data.Properties.ExpiresUtc.HasValue ? data.Properties.ExpiresUtc.Value.LocalDateTime : DateTime.Now.AddMinutes(_loginTimeoutMinutes),
|
||||
data.Properties.IsPersistent,
|
||||
"", //User data here!! This will come from the identity
|
||||
"/"
|
||||
userDataString,
|
||||
_cookiePath
|
||||
);
|
||||
|
||||
return FormsAuthentication.Encrypt(ticket);
|
||||
@@ -51,12 +54,14 @@ namespace Umbraco.Web.Security.Identity
|
||||
|
||||
var identity = new UmbracoBackOfficeIdentity(decrypt);
|
||||
|
||||
return new AuthenticationTicket(identity, new AuthenticationProperties
|
||||
var ticket = new AuthenticationTicket(identity, new AuthenticationProperties
|
||||
{
|
||||
ExpiresUtc = decrypt.Expiration.ToUniversalTime(),
|
||||
IssuedUtc = decrypt.IssueDate.ToUniversalTime(),
|
||||
IsPersistent = decrypt.IsPersistent
|
||||
});
|
||||
|
||||
return ticket;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace Umbraco.Web.Security.Identity
|
||||
ISecuritySection securitySection,
|
||||
int loginTimeoutMinutes,
|
||||
bool forceSsl,
|
||||
string umbracoPath,
|
||||
string cookiePath,
|
||||
bool useLegacyFormsAuthDataFormat = true)
|
||||
{
|
||||
AuthenticationType = "UmbracoBackOffice";
|
||||
@@ -30,7 +30,7 @@ namespace Umbraco.Web.Security.Identity
|
||||
if (useLegacyFormsAuthDataFormat)
|
||||
{
|
||||
//If this is not explicitly set it will fall back to the default automatically
|
||||
TicketDataFormat = new FormsAuthenticationSecureDataFormat(loginTimeoutMinutes);
|
||||
TicketDataFormat = new FormsAuthenticationSecureDataFormat(loginTimeoutMinutes, cookiePath);
|
||||
}
|
||||
|
||||
CookieDomain = securitySection.AuthCookieDomain;
|
||||
@@ -39,7 +39,7 @@ namespace Umbraco.Web.Security.Identity
|
||||
CookieSecure = forceSsl ? CookieSecureOption.Always : CookieSecureOption.SameAsRequest;
|
||||
|
||||
//Ensure the cookie path is set so that it isn't transmitted for anything apart from requests to the back office
|
||||
CookiePath = umbracoPath.EnsureStartsWith('/');
|
||||
CookiePath = cookiePath.EnsureStartsWith('/');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -14,6 +14,10 @@
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user