Merge pull request #2119 from umbraco/temp-U4-10274

U4-10274 Umbraco.MemberHasAccess isn't cached
This commit is contained in:
Sebastiaan Janssen
2017-09-01 15:45:55 +02:00
committed by GitHub
10 changed files with 120 additions and 56 deletions

View File

@@ -23,9 +23,9 @@ namespace Umbraco.Web.Models
private LoginStatusModel(bool doLookup)
{
if (doLookup && HttpContext.Current != null && ApplicationContext.Current != null)
if (doLookup && UmbracoContext.Current != null)
{
var helper = new MembershipHelper(ApplicationContext.Current, new HttpContextWrapper(HttpContext.Current));
var helper = new MembershipHelper(UmbracoContext.Current);
var model = helper.GetCurrentLoginStatus();
if (model != null)
{

View File

@@ -32,9 +32,9 @@ namespace Umbraco.Web.Models
private ProfileModel(bool doLookup)
{
MemberProperties = new List<UmbracoProperty>();
if (doLookup)
if (doLookup && UmbracoContext.Current != null)
{
var helper = new MembershipHelper(ApplicationContext.Current, new HttpContextWrapper(HttpContext.Current));
var helper = new MembershipHelper(UmbracoContext.Current);
var model = helper.GetCurrentMemberProfileModel();
MemberProperties = model.MemberProperties;
}

View File

@@ -32,9 +32,9 @@ namespace Umbraco.Web.Models
MemberProperties = new List<UmbracoProperty>();
LoginOnSuccess = true;
CreatePersistentLoginCookie = true;
if (doLookup && HttpContext.Current != null && ApplicationContext.Current != null)
if (doLookup && UmbracoContext.Current != null)
{
var helper = new MembershipHelper(ApplicationContext.Current, new HttpContextWrapper(HttpContext.Current));
var helper = new MembershipHelper(UmbracoContext.Current);
var model = helper.CreateRegistrationModel(MemberTypeAlias);
MemberProperties = model.MemberProperties;
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Security;
using Umbraco.Core;
@@ -63,7 +64,7 @@ namespace Umbraco.Web.Routing
Uri = uri;
RoutingContext = routingContext;
GetRolesForLogin = getRolesForLogin;
_getRolesForLoginCallback = getRolesForLogin;
_engine = new PublishedContentRequestEngine(
routingConfig,
@@ -446,8 +447,28 @@ namespace Umbraco.Web.Routing
/// Gets or sets the current RoutingContext.
/// </summary>
public RoutingContext RoutingContext { get; private set; }
/// <summary>
/// Returns the current members roles if a member is logged in
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
/// <remarks>
/// This ensures that the callback is only executed once in case this method is accessed a few times
/// </remarks>
public IEnumerable<string> GetRolesForLogin(string username)
{
string[] roles;
if (_rolesForLogin.TryGetValue(username, out roles))
return roles;
roles = _getRolesForLoginCallback(username).ToArray();
_rolesForLogin[username] = roles;
return roles;
}
internal Func<string, IEnumerable<string>> GetRolesForLogin { get; private set; }
private readonly IDictionary<string, string[]> _rolesForLogin = new Dictionary<string, string[]>();
private readonly Func<string, IEnumerable<string>> _getRolesForLoginCallback;
/// <summary>
/// The "umbraco page" object.

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
@@ -14,6 +15,7 @@ using Umbraco.Web.Models;
using Umbraco.Web.PublishedCache;
using Umbraco.Core.Cache;
using Umbraco.Web.Security.Providers;
using Umbraco.Core.Services;
using MPE = global::Umbraco.Core.Security.MembershipProviderExtensions;
namespace Umbraco.Web.Security
@@ -28,13 +30,19 @@ namespace Umbraco.Web.Security
private readonly RoleProvider _roleProvider;
private readonly ApplicationContext _applicationContext;
private readonly HttpContextBase _httpContext;
private readonly UmbracoContext _umbracoContext;
#region Constructors
[Obsolete("Use the constructor specifying an UmbracoContext")]
[EditorBrowsable(EditorBrowsableState.Never)]
public MembershipHelper(ApplicationContext applicationContext, HttpContextBase httpContext)
: this(applicationContext, httpContext, MPE.GetMembersMembershipProvider(), Roles.Enabled ? Roles.Provider : new MembersRoleProvider(applicationContext.Services.MemberService))
{
}
[Obsolete("Use the constructor specifying an UmbracoContext")]
[EditorBrowsable(EditorBrowsableState.Never)]
public MembershipHelper(ApplicationContext applicationContext, HttpContextBase httpContext, MembershipProvider membershipProvider, RoleProvider roleProvider)
{
if (applicationContext == null) throw new ArgumentNullException("applicationContext");
@@ -61,9 +69,58 @@ namespace Umbraco.Web.Security
_applicationContext = umbracoContext.Application;
_membershipProvider = membershipProvider;
_roleProvider = roleProvider;
_umbracoContext = umbracoContext;
}
#endregion
/// <summary>
/// Check if a document object is protected by the "Protect Pages" functionality in umbraco
/// </summary>
/// <param name="path">The full path of the document object to check</param>
/// <returns>True if the document object is protected</returns>
public virtual bool IsProtected(string path)
{
//this is a cached call
return _applicationContext.Services.PublicAccessService.IsProtected(path);
}
/// <summary>
/// Check if the current user has access to a document
/// </summary>
/// <param name="path">The full path of the document object to check</param>
/// <returns>True if the current user has access or if the current document isn't protected</returns>
public virtual bool MemberHasAccess(string path)
{
//cache this in the request cache
return _applicationContext.ApplicationCache.RequestCache.GetCacheItem<bool>(string.Format("{0}.{1}-{2}", typeof(MembershipHelper), "MemberHasAccess", path), () =>
{
if (IsProtected(path))
{
return IsLoggedIn() && HasAccess(path, Roles.Provider);
}
return true;
});
}
/// <summary>
/// This will check if the member has access to this path
/// </summary>
/// <param name="path"></param>
/// <param name="roleProvider"></param>
/// <returns></returns>
/// <remarks>
/// This is essentially the same as the PublicAccessServiceExtensions.HasAccess however this will use the PCR cache
/// of the already looked up roles for the member so this doesn't need to happen more than once.
/// This does a safety check in case of things like unit tests where there is no PCR and if that is the case it will use
/// lookup the roles directly.
/// </remarks>
private bool HasAccess(string path, RoleProvider roleProvider)
{
return _umbracoContext.PublishedContentRequest == null
? _applicationContext.Services.PublicAccessService.HasAccess(path, CurrentUserName, roleProvider.GetRolesForUser)
: _applicationContext.Services.PublicAccessService.HasAccess(path, CurrentUserName, _umbracoContext.PublishedContentRequest.GetRolesForLogin);
}
/// <summary>
/// Returns true if the current membership provider is the Umbraco built-in one.
/// </summary>

View File

@@ -48,11 +48,11 @@ namespace Umbraco.Web.Security
IEnumerable<string> allowGroups = null,
IEnumerable<int> allowMembers = null)
{
if (HttpContext.Current == null || ApplicationContext.Current == null)
if (UmbracoContext.Current == null)
{
return false;
}
var helper = new MembershipHelper(ApplicationContext.Current, new HttpContextWrapper(HttpContext.Current));
var helper = new MembershipHelper(UmbracoContext.Current);
return helper.IsMemberAuthorized(allowAll, allowTypes, allowGroups, allowMembers);
}

View File

@@ -17,7 +17,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using Umbraco.Core.Cache;
using Umbraco.Core.Cache;
namespace Umbraco.Web
{
@@ -433,7 +433,7 @@ namespace Umbraco.Web
/// <returns>True if the document object is protected</returns>
public bool IsProtected(string path)
{
return UmbracoContext.Application.Services.PublicAccessService.IsProtected(path);
return MembershipHelper.IsProtected(path);
}
[EditorBrowsable(EditorBrowsableState.Never)]
@@ -450,25 +450,7 @@ namespace Umbraco.Web
/// <returns>True if the current user has access or if the current document isn't protected</returns>
public bool MemberHasAccess(string path)
{
if (IsProtected(path))
{
return MembershipHelper.IsLoggedIn()
&& UmbracoContext.Application.Services.PublicAccessService.HasAccess(path, GetCurrentMember(), Roles.Provider);
}
return true;
}
/// <summary>
/// Gets (or adds) the current member from the current request cache
/// </summary>
private MembershipUser GetCurrentMember()
{
return UmbracoContext.Application.ApplicationCache.RequestCache
.GetCacheItem<MembershipUser>("UmbracoHelper.GetCurrentMember", () =>
{
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
return provider.GetCurrentUser();
});
return MembershipHelper.MemberHasAccess(path);
}
/// <summary>

View File

@@ -615,7 +615,7 @@ namespace umbraco.cms.presentation.user
// update when the AD provider is active.
if ((BackOfficeProvider is ActiveDirectoryMembershipProvider) == false)
{
var membershipHelper = new MembershipHelper(ApplicationContext, new HttpContextWrapper(Context));
var membershipHelper = new MembershipHelper(UmbracoContext.Current);
//set the writable properties that we are editing
membershipHelper.UpdateMember(membershipUser, BackOfficeProvider,
email.Text.Trim(),