Merge pull request #2119 from umbraco/temp-U4-10274
U4-10274 Umbraco.MemberHasAccess isn't cached
This commit is contained in:
@@ -184,30 +184,16 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public IEnumerable<IMemberGroup> GetMemberGroupsForMember(string username)
|
||||
{
|
||||
//find the member by username
|
||||
var memberSql = new Sql();
|
||||
var memberObjectType = new Guid(Constants.ObjectTypes.Member);
|
||||
var sql = new Sql()
|
||||
.Select("un.*")
|
||||
.From("umbracoNode AS un")
|
||||
.InnerJoin("cmsMember2MemberGroup")
|
||||
.On("un.id = cmsMember2MemberGroup.MemberGroup")
|
||||
.LeftJoin("(SELECT umbracoNode.id, cmsMember.LoginName FROM umbracoNode INNER JOIN cmsMember ON umbracoNode.id = cmsMember.nodeId) AS member")
|
||||
.On("member.id = cmsMember2MemberGroup.Member")
|
||||
.Where("un.nodeObjectType=@objectType", new {objectType = NodeObjectTypeId })
|
||||
.Where("member.LoginName=@loginName", new {loginName = username});
|
||||
|
||||
memberSql.Select("umbracoNode.id")
|
||||
.From<NodeDto>()
|
||||
.InnerJoin<MemberDto>()
|
||||
.On<NodeDto, MemberDto>(dto => dto.NodeId, dto => dto.NodeId)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == memberObjectType)
|
||||
.Where<MemberDto>(x => x.LoginName == username);
|
||||
var memberIdUsername = Database.Fetch<int?>(memberSql).FirstOrDefault();
|
||||
if (memberIdUsername.HasValue == false)
|
||||
{
|
||||
return Enumerable.Empty<IMemberGroup>();
|
||||
}
|
||||
|
||||
var sql = new Sql();
|
||||
sql.Select("umbracoNode.*")
|
||||
.From<NodeDto>()
|
||||
.InnerJoin<Member2MemberGroupDto>()
|
||||
.On<NodeDto, Member2MemberGroupDto>(dto => dto.NodeId, dto => dto.MemberGroup)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
|
||||
.Where<Member2MemberGroupDto>(x => x.Member == memberIdUsername.Value);
|
||||
|
||||
return Database.Fetch<NodeDto>(sql)
|
||||
.DistinctBy(dto => dto.NodeId)
|
||||
.Select(x => _modelFactory.BuildEntity(x));
|
||||
|
||||
@@ -72,10 +72,28 @@ namespace Umbraco.Core.Services
|
||||
|
||||
public static bool HasAccess(this IPublicAccessService publicAccessService, string path, MembershipUser member, RoleProvider roleProvider)
|
||||
{
|
||||
return publicAccessService.HasAccess(path, member.UserName, roleProvider.GetRolesForUser);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the member with the specified username has access to the path which is also based on the passed in roles for the member
|
||||
/// </summary>
|
||||
/// <param name="publicAccessService"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="username"></param>
|
||||
/// <param name="rolesCallback">A callback to retrieve the roles for this member</param>
|
||||
/// <returns></returns>
|
||||
public static bool HasAccess(this IPublicAccessService publicAccessService, string path, string username, Func<string, IEnumerable<string>> rolesCallback)
|
||||
{
|
||||
if (rolesCallback == null) throw new ArgumentNullException("roles");
|
||||
if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value cannot be null or whitespace.", "username");
|
||||
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Value cannot be null or whitespace.", "path");
|
||||
|
||||
var entry = publicAccessService.GetEntryForContent(path.EnsureEndsWith(path));
|
||||
if (entry == null) return true;
|
||||
|
||||
var roles = roleProvider.GetRolesForUser(member.UserName);
|
||||
var roles = rolesCallback(username);
|
||||
|
||||
return entry.Rules.Any(x => x.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType
|
||||
&& roles.Contains(x.RuleValue));
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user