diff --git a/src/Umbraco.Core/Services/PublicAccessServiceExtensions.cs b/src/Umbraco.Core/Services/PublicAccessServiceExtensions.cs
index 0b682b2b6e..d159e3ae64 100644
--- a/src/Umbraco.Core/Services/PublicAccessServiceExtensions.cs
+++ b/src/Umbraco.Core/Services/PublicAccessServiceExtensions.cs
@@ -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);
+ }
+
+ ///
+ /// 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
+ ///
+ ///
+ ///
+ ///
+ /// A callback to retrieve the roles for this member
+ ///
+ public static bool HasAccess(this IPublicAccessService publicAccessService, string path, string username, Func> 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));
}
diff --git a/src/Umbraco.Web/Models/LoginStatusModel.cs b/src/Umbraco.Web/Models/LoginStatusModel.cs
index e10b42b096..23fb2039e9 100644
--- a/src/Umbraco.Web/Models/LoginStatusModel.cs
+++ b/src/Umbraco.Web/Models/LoginStatusModel.cs
@@ -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)
{
diff --git a/src/Umbraco.Web/Models/ProfileModel.cs b/src/Umbraco.Web/Models/ProfileModel.cs
index 118f9a9f7a..39043a4b11 100644
--- a/src/Umbraco.Web/Models/ProfileModel.cs
+++ b/src/Umbraco.Web/Models/ProfileModel.cs
@@ -32,9 +32,9 @@ namespace Umbraco.Web.Models
private ProfileModel(bool doLookup)
{
MemberProperties = new List();
- 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;
}
diff --git a/src/Umbraco.Web/Models/RegisterModel.cs b/src/Umbraco.Web/Models/RegisterModel.cs
index 237f6d7845..b51f09b631 100644
--- a/src/Umbraco.Web/Models/RegisterModel.cs
+++ b/src/Umbraco.Web/Models/RegisterModel.cs
@@ -32,9 +32,9 @@ namespace Umbraco.Web.Models
MemberProperties = new List();
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;
}
diff --git a/src/Umbraco.Web/Routing/PublishedContentRequest.cs b/src/Umbraco.Web/Routing/PublishedContentRequest.cs
index 9c21958603..641821ddd6 100644
--- a/src/Umbraco.Web/Routing/PublishedContentRequest.cs
+++ b/src/Umbraco.Web/Routing/PublishedContentRequest.cs
@@ -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.
///
public RoutingContext RoutingContext { get; private set; }
+
+ ///
+ /// Returns the current members roles if a member is logged in
+ ///
+ ///
+ ///
+ ///
+ /// This ensures that the callback is only executed once in case this method is accessed a few times
+ ///
+ public IEnumerable GetRolesForLogin(string username)
+ {
+ string[] roles;
+ if (_rolesForLogin.TryGetValue(username, out roles))
+ return roles;
+
+ roles = _getRolesForLoginCallback(username).ToArray();
+ _rolesForLogin[username] = roles;
+ return roles;
+ }
- internal Func> GetRolesForLogin { get; private set; }
+ private readonly IDictionary _rolesForLogin = new Dictionary();
+ private readonly Func> _getRolesForLoginCallback;
///
/// The "umbraco page" object.
diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs
index 605b5137d8..e43adff8b4 100644
--- a/src/Umbraco.Web/Security/MembershipHelper.cs
+++ b/src/Umbraco.Web/Security/MembershipHelper.cs
@@ -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
+ ///
+ /// Check if a document object is protected by the "Protect Pages" functionality in umbraco
+ ///
+ /// The full path of the document object to check
+ /// True if the document object is protected
+ public virtual bool IsProtected(string path)
+ {
+ //this is a cached call
+ return _applicationContext.Services.PublicAccessService.IsProtected(path);
+ }
+
+ ///
+ /// Check if the current user has access to a document
+ ///
+ /// The full path of the document object to check
+ /// True if the current user has access or if the current document isn't protected
+ public virtual bool MemberHasAccess(string path)
+ {
+ //cache this in the request cache
+ return _applicationContext.ApplicationCache.RequestCache.GetCacheItem(string.Format("{0}.{1}-{2}", typeof(MembershipHelper), "MemberHasAccess", path), () =>
+ {
+ if (IsProtected(path))
+ {
+ return IsLoggedIn() && HasAccess(path, Roles.Provider);
+ }
+ return true;
+ });
+ }
+
+ ///
+ /// This will check if the member has access to this path
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ 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);
+ }
+
///
/// Returns true if the current membership provider is the Umbraco built-in one.
///
diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs
index cda9f04fad..b12b6ccde0 100644
--- a/src/Umbraco.Web/Security/WebSecurity.cs
+++ b/src/Umbraco.Web/Security/WebSecurity.cs
@@ -48,11 +48,11 @@ namespace Umbraco.Web.Security
IEnumerable allowGroups = null,
IEnumerable 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);
}
diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs
index 6f3da17254..28960eb07d 100644
--- a/src/Umbraco.Web/UmbracoHelper.cs
+++ b/src/Umbraco.Web/UmbracoHelper.cs
@@ -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
/// True if the document object is protected
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
/// True if the current user has access or if the current document isn't protected
public bool MemberHasAccess(string path)
{
- if (IsProtected(path))
- {
- return MembershipHelper.IsLoggedIn()
- && UmbracoContext.Application.Services.PublicAccessService.HasAccess(path, GetCurrentMember(), Roles.Provider);
- }
- return true;
- }
-
- ///
- /// Gets (or adds) the current member from the current request cache
- ///
- private MembershipUser GetCurrentMember()
- {
- return UmbracoContext.Application.ApplicationCache.RequestCache
- .GetCacheItem("UmbracoHelper.GetCurrentMember", () =>
- {
- var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
- return provider.GetCurrentUser();
- });
+ return MembershipHelper.MemberHasAccess(path);
}
///
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
index 91a8677c81..3a0dc0d274 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
@@ -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(),