Isolated more web usages - Membership and Request stuff

- Moved a few more files
This commit is contained in:
Bjarke Berg
2020-02-28 11:15:25 +01:00
parent dc36fa1290
commit 7daee53c64
26 changed files with 369 additions and 174 deletions

View File

@@ -7,4 +7,5 @@ namespace Umbraco.Core.Cookie
void SetCookieValue(string cookieName, string value);
bool HasCookie(string cookieName);
}
}

View File

@@ -0,0 +1,8 @@
namespace Umbraco.Core.Request
{
public interface IRequestAccessor
{
string GetRequestValue(string name);
string GetQueryStringValue(string culture);
}
}

View File

@@ -4,6 +4,7 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Models.PublishedContent;
using System.Globalization;
using Umbraco.Core.Request;
namespace Umbraco.Web.Routing
{
@@ -16,14 +17,14 @@ namespace Umbraco.Web.Routing
public class ContentFinderByIdPath : IContentFinder
{
private readonly ILogger _logger;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IRequestAccessor _requestAccessor;
private readonly IWebRoutingSection _webRoutingSection;
public ContentFinderByIdPath(IWebRoutingSection webRoutingSection, ILogger logger, IHttpContextAccessor httpContextAccessor)
public ContentFinderByIdPath(IWebRoutingSection webRoutingSection, ILogger logger, IRequestAccessor requestAccessor)
{
_webRoutingSection = webRoutingSection ?? throw new System.ArgumentNullException(nameof(webRoutingSection));
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
_httpContextAccessor = httpContextAccessor;
_requestAccessor = requestAccessor;
}
/// <summary>
@@ -56,12 +57,14 @@ namespace Umbraco.Web.Routing
if (node != null)
{
var httpContext = _httpContextAccessor.GetRequiredHttpContext();
var cultureFromQuerystring = _requestAccessor.GetQueryStringValue("culture");
//if we have a node, check if we have a culture in the query string
if (httpContext.Request.QueryString.ContainsKey("culture"))
if (!string.IsNullOrEmpty(cultureFromQuerystring))
{
//we're assuming it will match a culture, if an invalid one is passed in, an exception will throw (there is no TryGetCultureInfo method), i think this is ok though
frequest.Culture = CultureInfo.GetCultureInfo(httpContext.Request.QueryString["culture"]);
frequest.Culture = CultureInfo.GetCultureInfo(cultureFromQuerystring);
}
frequest.PublishedContent = node;

View File

@@ -1,4 +1,6 @@
namespace Umbraco.Web.Routing
using Umbraco.Core.Request;
namespace Umbraco.Web.Routing
{
/// <summary>
/// This looks up a document by checking for the umbPageId of a request/query string
@@ -9,17 +11,17 @@
/// </remarks>
public class ContentFinderByPageIdQuery : IContentFinder
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IRequestAccessor _requestAccessor;
public ContentFinderByPageIdQuery(IHttpContextAccessor httpContextAccessor)
public ContentFinderByPageIdQuery(IRequestAccessor requestAccessor)
{
_httpContextAccessor = httpContextAccessor;
_requestAccessor = requestAccessor;
}
public bool TryFindContent(IPublishedRequest frequest)
{
int pageId;
if (int.TryParse(_httpContextAccessor.GetRequiredHttpContext().Request["umbPageID"], out pageId))
if (int.TryParse(_requestAccessor.GetRequestValue("umbPageID"), out pageId))
{
var doc = frequest.UmbracoContext.Content.GetById(pageId);

View File

@@ -4,13 +4,12 @@ using System.Threading;
using System.Globalization;
using System.IO;
using Umbraco.Core;
using Umbraco.Web.Composing;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Request;
using Umbraco.Core.Services;
using Umbraco.Web.Macros;
using Umbraco.Web.Security;
namespace Umbraco.Web.Routing
@@ -23,13 +22,17 @@ namespace Umbraco.Web.Routing
private readonly IWebRoutingSection _webRoutingSection;
private readonly ContentFinderCollection _contentFinders;
private readonly IContentLastChanceFinder _contentLastChanceFinder;
private readonly ServiceContext _services;
private readonly IProfilingLogger _profilingLogger;
private readonly IVariationContextAccessor _variationContextAccessor;
private readonly ILogger _logger;
private readonly IUmbracoSettingsSection _umbracoSettingsSection;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IPublishedUrlProvider _publishedUrlProvider;
private readonly IRequestAccessor _requestAccessor;
private readonly IPublishedValueFallback _publishedValueFallback;
private readonly IPublicAccessChecker _publicAccessChecker;
private readonly IFileService _fileService;
private readonly IContentTypeService _contentTypeService;
private readonly IPublicAccessService _publicAccessService;
/// <summary>
/// Initializes a new instance of the <see cref="PublishedRouter"/> class.
@@ -39,22 +42,30 @@ namespace Umbraco.Web.Routing
ContentFinderCollection contentFinders,
IContentLastChanceFinder contentLastChanceFinder,
IVariationContextAccessor variationContextAccessor,
ServiceContext services,
IProfilingLogger proflog,
IUmbracoSettingsSection umbracoSettingsSection,
IHttpContextAccessor httpContextAccessor,
IPublishedUrlProvider publishedUrlProvider)
IPublishedUrlProvider publishedUrlProvider,
IRequestAccessor requestAccessor,
IPublishedValueFallback publishedValueFallback,
IPublicAccessChecker publicAccessChecker,
IFileService fileService,
IContentTypeService contentTypeService,
IPublicAccessService publicAccessService)
{
_webRoutingSection = webRoutingSection ?? throw new ArgumentNullException(nameof(webRoutingSection));
_contentFinders = contentFinders ?? throw new ArgumentNullException(nameof(contentFinders));
_contentLastChanceFinder = contentLastChanceFinder ?? throw new ArgumentNullException(nameof(contentLastChanceFinder));
_services = services ?? throw new ArgumentNullException(nameof(services));
_profilingLogger = proflog ?? throw new ArgumentNullException(nameof(proflog));
_variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor));
_logger = proflog;
_umbracoSettingsSection = umbracoSettingsSection ?? throw new ArgumentNullException(nameof(umbracoSettingsSection));
_httpContextAccessor = httpContextAccessor;
_publishedUrlProvider = publishedUrlProvider;
_requestAccessor = requestAccessor;
_publishedValueFallback = publishedValueFallback;
_publicAccessChecker = publicAccessChecker;
_fileService = fileService;
_contentTypeService = contentTypeService;
_publicAccessService = publicAccessService;
}
/// <inheritdoc />
@@ -358,7 +369,7 @@ namespace Umbraco.Web.Routing
/// <inheritdoc />
public ITemplate GetTemplate(string alias)
{
return _services.FileService.GetTemplate(alias);
return _fileService.GetTemplate(alias);
}
/// <summary>
@@ -498,7 +509,7 @@ namespace Umbraco.Web.Routing
var redirect = false;
var valid = false;
IPublishedContent internalRedirectNode = null;
var internalRedirectId = request.PublishedContent.Value(Constants.Conventions.Content.InternalRedirectId, defaultValue: -1);
var internalRedirectId = request.PublishedContent.Value(_publishedValueFallback, Constants.Conventions.Content.InternalRedirectId, defaultValue: -1);
if (internalRedirectId > 0)
{
@@ -508,7 +519,7 @@ namespace Umbraco.Web.Routing
}
else
{
var udiInternalRedirectId = request.PublishedContent.Value<GuidUdi>(Constants.Conventions.Content.InternalRedirectId);
var udiInternalRedirectId = request.PublishedContent.Value<GuidUdi>(_publishedValueFallback, Constants.Conventions.Content.InternalRedirectId);
if (udiInternalRedirectId != null)
{
// try and get the redirect node from a UDI Guid
@@ -555,59 +566,66 @@ namespace Umbraco.Web.Routing
var path = request.PublishedContent.Path;
var publicAccessAttempt = _services.PublicAccessService.IsProtected(path);
var publicAccessAttempt = _publicAccessService.IsProtected(path);
if (publicAccessAttempt)
{
_logger.Debug<PublishedRouter>("EnsurePublishedContentAccess: Page is protected, check for access");
var membershipHelper = Current.Factory.GetInstance<MembershipHelper>();
if (membershipHelper.IsLoggedIn() == false)
var status = _publicAccessChecker.HasMemberAccessToContent(request.PublishedContent.Id);
switch (status)
{
_logger.Debug<PublishedRouter>("EnsurePublishedContentAccess: Not logged in, redirect to login page");
var loginPageId = publicAccessAttempt.Result.LoginNodeId;
if (loginPageId != request.PublishedContent.Id)
request.PublishedContent = request.UmbracoContext.PublishedSnapshot.Content.GetById(loginPageId);
}
else if (_services.PublicAccessService.HasAccess(request.PublishedContent.Id, _services.ContentService, membershipHelper.CurrentUserName, membershipHelper.GetCurrentUserRoles()) == false)
{
_logger.Debug<PublishedRouter>("EnsurePublishedContentAccess: Current member has not access, redirect to error page");
var errorPageId = publicAccessAttempt.Result.NoAccessNodeId;
if (errorPageId != request.PublishedContent.Id)
request.PublishedContent = request.UmbracoContext.PublishedSnapshot.Content.GetById(errorPageId);
}
else
{
// grab the current member
var member = membershipHelper.GetCurrentMember();
// if the member has the "approved" and/or "locked out" properties, make sure they're correctly set before allowing access
var memberIsActive = true;
if (member != null)
{
if (member.HasProperty(Constants.Conventions.Member.IsApproved) == false)
memberIsActive = member.Value<bool>(Constants.Conventions.Member.IsApproved);
if (member.HasProperty(Constants.Conventions.Member.IsLockedOut) == false)
memberIsActive = member.Value<bool>(Constants.Conventions.Member.IsLockedOut) == false;
}
if (memberIsActive == false)
{
_logger.Debug<PublishedRouter>(
"Current member is either unapproved or locked out, redirect to error page");
var errorPageId = publicAccessAttempt.Result.NoAccessNodeId;
if (errorPageId != request.PublishedContent.Id)
request.PublishedContent =
request.UmbracoContext.PublishedSnapshot.Content.GetById(errorPageId);
}
else
{
case PublicAccessStatus.NotLoggedIn:
_logger.Debug<PublishedRouter>("EnsurePublishedContentAccess: Not logged in, redirect to login page");
SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.LoginNodeId);
break;
case PublicAccessStatus.AccessDenied:
_logger.Debug<PublishedRouter>("EnsurePublishedContentAccess: Current member has not access, redirect to error page");
SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId);
break;
case PublicAccessStatus.LockedOut:
_logger.Debug<PublishedRouter>("Current member is locked out, redirect to error page");
SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId);
break;
case PublicAccessStatus.NotApproved:
_logger.Debug<PublishedRouter>("Current member is unapproved, redirect to error page");
SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId);
break;
case PublicAccessStatus.AccessAccepted:
_logger.Debug<PublishedRouter>("Current member has access");
}
break;
}
//
// else
// {
// // grab the current member
// var member = _membershipHelper.GetCurrentMember();
// // if the member has the "approved" and/or "locked out" properties, make sure they're correctly set before allowing access
// var memberIsActive = true;
// if (member != null)
// {
// if (member.HasProperty(Constants.Conventions.Member.IsApproved) == false)
// memberIsActive = member.Value<bool>(Constants.Conventions.Member.IsApproved);
//
// if (member.HasProperty(Constants.Conventions.Member.IsLockedOut) == false)
// memberIsActive = member.Value<bool>(Constants.Conventions.Member.IsLockedOut) == false;
// }
//
// if (memberIsActive == false)
// {
// _logger.Debug<PublishedRouter>(
// "Current member is either unapproved or locked out, redirect to error page");
// var errorPageId = publicAccessAttempt.Result.NoAccessNodeId;
// if (errorPageId != request.PublishedContent.Id)
// request.PublishedContent =
// request.UmbracoContext.PublishedSnapshot.Content.GetById(errorPageId);
// }
// else
// {
// _logger.Debug<PublishedRouter>("Current member has access");
// }
// }
}
else
{
@@ -615,6 +633,12 @@ namespace Umbraco.Web.Routing
}
}
private static void SetPublishedContentAsOtherPage(IPublishedRequest request, int errorPageId)
{
if (errorPageId != request.PublishedContent.Id)
request.PublishedContent = request.UmbracoContext.PublishedSnapshot.Content.GetById(errorPageId);
}
/// <summary>
/// Finds a template for the current node, if any.
/// </summary>
@@ -637,7 +661,7 @@ namespace Umbraco.Web.Routing
var useAltTemplate = request.IsInitialPublishedContent
|| (_webRoutingSection.InternalRedirectPreservesTemplate && request.IsInternalRedirectPublishedContent);
var altTemplate = useAltTemplate
? _httpContextAccessor.GetRequiredHttpContext().Request[Constants.Conventions.Url.AltTemplate]
? _requestAccessor.GetRequestValue(Constants.Conventions.Url.AltTemplate)
: null;
if (string.IsNullOrWhiteSpace(altTemplate))
@@ -674,10 +698,15 @@ namespace Umbraco.Web.Routing
_logger.Debug<PublishedRouter>("FindTemplate: Look for alternative template alias={AltTemplate}", altTemplate);
// IsAllowedTemplate deals both with DisableAlternativeTemplates and ValidateAlternativeTemplates settings
if (request.PublishedContent.IsAllowedTemplate(altTemplate))
if (request.PublishedContent.IsAllowedTemplate(
_fileService,
_contentTypeService,
_umbracoSettingsSection.WebRouting.DisableAlternativeTemplates,
_umbracoSettingsSection.WebRouting.ValidateAlternativeTemplates,
altTemplate))
{
// allowed, use
var template = _services.FileService.GetTemplate(altTemplate);
var template = _fileService.GetTemplate(altTemplate);
if (template != null)
{
@@ -731,7 +760,7 @@ namespace Umbraco.Web.Routing
if (templateId == null)
throw new InvalidOperationException("The template is not set, the page cannot render.");
var template = _services.FileService.GetTemplate(templateId.Value);
var template = _fileService.GetTemplate(templateId.Value);
if (template == null)
throw new InvalidOperationException("The template with Id " + templateId + " does not exist, the page cannot render.");
_logger.Debug<PublishedRouter>("GetTemplateModel: Got template id={TemplateId} alias={TemplateAlias}", template.Id, template.Alias);
@@ -750,7 +779,7 @@ namespace Umbraco.Web.Routing
if (request.PublishedContent.HasProperty(Constants.Conventions.Content.Redirect) == false)
return;
var redirectId = request.PublishedContent.Value(Constants.Conventions.Content.Redirect, defaultValue: -1);
var redirectId = request.PublishedContent.Value(_publishedValueFallback, Constants.Conventions.Content.Redirect, defaultValue: -1);
var redirectUrl = "#";
if (redirectId > 0)
{
@@ -759,7 +788,7 @@ namespace Umbraco.Web.Routing
else
{
// might be a UDI instead of an int Id
var redirectUdi = request.PublishedContent.Value<GuidUdi>(Constants.Conventions.Content.Redirect);
var redirectUdi = request.PublishedContent.Value<GuidUdi>(_publishedValueFallback, Constants.Conventions.Content.Redirect);
if (redirectUdi != null)
redirectUrl = _publishedUrlProvider.GetUrl(redirectUdi.Guid);
}

View File

@@ -0,0 +1,7 @@
namespace Umbraco.Core.Security
{
public interface IMemberUserKeyProvider
{
object GetMemberProviderUserKey();
}
}

View File

@@ -0,0 +1,7 @@
namespace Umbraco.Web.Security
{
public interface IPublicAccessChecker
{
PublicAccessStatus HasMemberAccessToContent(int publishedContentId);
}
}

View File

@@ -0,0 +1,11 @@
namespace Umbraco.Web.Security
{
public enum PublicAccessStatus
{
NotLoggedIn,
AccessDenied,
NotApproved,
LockedOut,
AccessAccepted
}
}

View File

@@ -0,0 +1,8 @@
namespace Umbraco.Core.Session
{
public interface ISessionManager
{
object GetSessionValue(string sessionName);
void SetSessionValue(string sessionName, object value);
}
}

View File

@@ -2,7 +2,6 @@
using Umbraco.Core.Cookie;
using Umbraco.Core.Migrations;
namespace Umbraco.Web.Migrations.PostMigrations
{
/// <summary>

View File

@@ -6,6 +6,7 @@ using Umbraco.Core.Composing;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
using Umbraco.Web.PublishedCache;
@@ -26,12 +27,14 @@ namespace Umbraco.Web.Routing
private readonly IUmbracoSettingsSection _umbracoSettings;
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
private readonly IRedirectUrlService _redirectUrlService;
private readonly IVariationContextAccessor _variationContextAccessor;
public RedirectTrackingComponent(IUmbracoSettingsSection umbracoSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService)
public RedirectTrackingComponent(IUmbracoSettingsSection umbracoSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService, IVariationContextAccessor variationContextAccessor)
{
_umbracoSettings = umbracoSettings;
_publishedSnapshotAccessor = publishedSnapshotAccessor;
_redirectUrlService = redirectUrlService;
_variationContextAccessor = variationContextAccessor;
}
public void Initialize()
@@ -99,12 +102,12 @@ namespace Umbraco.Web.Routing
{
var contentCache = _publishedSnapshotAccessor.PublishedSnapshot.Content;
var entityContent = contentCache.GetById(entity.Id);
if (entityContent == null) return;
if (entityContent == null) return;
// get the default affected cultures by going up the tree until we find the first culture variant entity (default to no cultures)
// get the default affected cultures by going up the tree until we find the first culture variant entity (default to no cultures)
var defaultCultures = entityContent.AncestorsOrSelf()?.FirstOrDefault(a => a.Cultures.Any())?.Cultures.Keys.ToArray()
?? new[] { (string)null };
foreach (var x in entityContent.DescendantsOrSelf())
foreach (var x in entityContent.DescendantsOrSelf(_variationContextAccessor))
{
// if this entity defines specific cultures, use those instead of the default ones
var cultures = x.Cultures.Any() ? x.Cultures.Keys : defaultCultures;

View File

@@ -1,10 +1,8 @@
using Moq;
using NUnit.Framework;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Request;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.Routing;
namespace Umbraco.Tests.Routing
@@ -20,7 +18,7 @@ namespace Umbraco.Tests.Routing
var umbracoContext = GetUmbracoContext(urlAsString);
var publishedRouter = CreatePublishedRouter();
var frequest = publishedRouter.CreateRequest(umbracoContext);
var lookup = new ContentFinderByIdPath(Factory.GetInstance<IUmbracoSettingsSection>().WebRouting, Logger, HttpContextAccessor);
var lookup = new ContentFinderByIdPath(Factory.GetInstance<IUmbracoSettingsSection>().WebRouting, Logger, Factory.GetInstance<IRequestAccessor>());
var result = lookup.TryFindContent(frequest);

View File

@@ -1,5 +1,6 @@
using Moq;
using NUnit.Framework;
using Umbraco.Core.Request;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.Routing;
@@ -20,15 +21,10 @@ namespace Umbraco.Tests.Routing
var httpContext = GetHttpContextFactory(urlAsString).HttpContext;
var publishedRouter = CreatePublishedRouter();
var frequest = publishedRouter.CreateRequest(umbracoContext);
var mockHttpContextAccessor = new Mock<IHttpContextAccessor>();
mockHttpContextAccessor.Setup(x => x.HttpContext).Returns(httpContext);
var lookup = new ContentFinderByPageIdQuery(mockHttpContextAccessor.Object);
var mockRequestAccessor = new Mock<IRequestAccessor>();
mockRequestAccessor.Setup(x => x.GetRequestValue("umbPageID")).Returns(httpContext.Request.QueryString["umbPageID"]);
//we need to manually stub the return output of HttpContext.Request["umbPageId"]
var requestMock = Mock.Get(httpContext.Request);
requestMock.Setup(x => x["umbPageID"])
.Returns(httpContext.Request.QueryString["umbPageID"]);
var lookup = new ContentFinderByPageIdQuery(mockRequestAccessor.Object);
var result = lookup.TryFindContent(frequest);

View File

@@ -10,7 +10,9 @@ using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Request;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
using Umbraco.Core.Strings;
using Umbraco.Tests.PublishedContent;
using Umbraco.Tests.TestHelpers.Stubs;
@@ -19,6 +21,7 @@ using Umbraco.Web;
using Umbraco.Web.Composing;
using Umbraco.Web.Models.PublishedContent;
using Umbraco.Web.Routing;
using Umbraco.Web.Security;
namespace Umbraco.Tests.TestHelpers
{
@@ -97,12 +100,16 @@ namespace Umbraco.Tests.TestHelpers
contentFinders ?? new ContentFinderCollection(Enumerable.Empty<IContentFinder>()),
new TestLastChanceFinder(),
new TestVariationContextAccessor(),
container?.TryGetInstance<ServiceContext>() ?? ServiceContext.CreatePartial(),
new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()),
container?.TryGetInstance<IUmbracoSettingsSection>() ?? Current.Factory.GetInstance<IUmbracoSettingsSection>(),
Mock.Of<IHttpContextAccessor>(),
Mock.Of<IPublishedUrlProvider>()
);
Mock.Of<IPublishedUrlProvider>(),
Mock.Of<IRequestAccessor>(),
container?.GetInstance<IPublishedValueFallback>() ?? Current.Factory.GetInstance<IPublishedValueFallback>(),
container?.GetInstance<IPublicAccessChecker>()?? Current.Factory.GetInstance<IPublicAccessChecker>(),
container?.GetInstance<IFileService>()?? Current.Factory.GetInstance<IFileService>(),
container?.GetInstance<IContentTypeService>() ?? Current.Factory.GetInstance<IContentTypeService>(),
container?.GetInstance<IPublicAccessService>() ?? Current.Factory.GetInstance<IPublicAccessService>()
);
}
}
}

View File

@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Web.Routing;
using System.Web.Security;
using System.Xml.Linq;
using Examine;
using Moq;
@@ -51,12 +52,15 @@ using Umbraco.Web.Templates;
using Umbraco.Web.PropertyEditors;
using Umbraco.Core.Dictionary;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Request;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Net;
using Umbraco.Tests.LegacyXmlPublishedCache;
using Umbraco.Web.AspNet;
using Umbraco.Web.Install;
using Umbraco.Web.Security;
using Umbraco.Web.Security.Providers;
using Umbraco.Web.Trees;
using Current = Umbraco.Web.Composing.Current;
namespace Umbraco.Tests.Testing
@@ -203,6 +207,18 @@ namespace Umbraco.Tests.Testing
Composition.RegisterUnique(ipResolver);
Composition.RegisterUnique<IPasswordHasher, AspNetPasswordHasher>();
Composition.RegisterUnique(TestHelper.ShortStringHelper);
Composition.RegisterUnique<IRequestAccessor, AspNetRequestAccessor>();
Composition.RegisterUnique<IPublicAccessChecker, PublicAccessChecker>();
var memberService = Mock.Of<IMemberService>();
var memberTypeService = Mock.Of<IMemberTypeService>();
var membershipProvider = new MembersMembershipProvider(memberService, memberTypeService, Mock.Of<IUmbracoVersion>(), TestHelper.GetHostingEnvironment(), TestHelper.GetIpResolver());
var membershipHelper = new MembershipHelper(Mock.Of<IHttpContextAccessor>(), Mock.Of<IPublishedMemberCache>(), membershipProvider, Mock.Of<RoleProvider>(), memberService, memberTypeService, Mock.Of<IPublicAccessService>(), AppCaches.Disabled, logger, ShortStringHelper, Mock.Of<IEntityService>());
Composition.RegisterUnique(membershipHelper);
TestObjects = new TestObjects(register);

View File

@@ -1,38 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<processing preserveExifMetaData="true" fixGamma="false" interceptAllRequests="false" allowCacheBuster="true">
<plugins>
<plugin name="Gamma" type="ImageProcessor.Web.Processors.Gamma, ImageProcessor.Web" />
<plugin name="Gamma" type="ImageProcessor.Web.Processors.Gamma, ImageProcessor.Web" />
<plugin name="Alpha" type="ImageProcessor.Web.Processors.Alpha, ImageProcessor.Web" /><plugin name="AutoRotate" type="ImageProcessor.Web.Processors.AutoRotate, ImageProcessor.Web" enabled="true" /><plugin name="Background" type="ImageProcessor.Web.Processors.Background, ImageProcessor.Web">
<settings>
<setting key="VirtualPath" value="~/images/imageprocessor/background/" />
@@ -63,4 +35,4 @@
<setting key="MaxHeight" value="5000" />
</settings>
</plugin><plugin name="Rotate" type="ImageProcessor.Web.Processors.Rotate, ImageProcessor.Web" /><plugin name="RotateBounded" type="ImageProcessor.Web.Processors.RotateBounded, ImageProcessor.Web" /><plugin name="RoundedCorners" type="ImageProcessor.Web.Processors.RoundedCorners, ImageProcessor.Web" /><plugin name="Saturation" type="ImageProcessor.Web.Processors.Saturation, ImageProcessor.Web" /><plugin name="Tint" type="ImageProcessor.Web.Processors.Tint, ImageProcessor.Web" /><plugin name="Vignette" type="ImageProcessor.Web.Processors.Vignette, ImageProcessor.Web" /><plugin name="Watermark" type="ImageProcessor.Web.Processors.Watermark, ImageProcessor.Web" /></plugins><presets>
</presets></processing>
</presets></processing>

View File

@@ -0,0 +1,24 @@
using Umbraco.Core.Request;
namespace Umbraco.Web.AspNet
{
public class AspNetRequestAccessor : IRequestAccessor
{
private readonly IHttpContextAccessor _httpContextAccessor;
public AspNetRequestAccessor(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string GetRequestValue(string name)
{
return _httpContextAccessor.GetRequiredHttpContext().Request[name];
}
public string GetQueryStringValue(string name)
{
return _httpContextAccessor.GetRequiredHttpContext().Request.QueryString[name];
}
}
}

View File

@@ -1,11 +0,0 @@
using System.Web;
using Umbraco.Core;
using Umbraco.Net;
namespace Umbraco.Web
{
internal class AspNetSessionIdResolver : ISessionIdResolver
{
public string SessionId => HttpContext.Current?.Session?.SessionID;
}
}

View File

@@ -0,0 +1,26 @@
using System.Web;
using Umbraco.Core.Session;
using Umbraco.Net;
namespace Umbraco.Web.AspNet
{
public class AspNetSessionManager: ISessionManager, ISessionIdResolver
{
public AspNetSessionManager()
{
}
public object GetSessionValue(string sessionName)
{
return HttpContext.Current.Session[sessionName];
}
public void SetSessionValue(string sessionName, object value)
{
HttpContext.Current.Session[sessionName] = value;
}
public string SessionId => HttpContext.Current?.Session?.SessionID;
}
}

49
src/Umbraco.Web/Macros/MacroRenderer.cs Executable file → Normal file
View File

@@ -1,5 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -12,10 +11,11 @@ using Umbraco.Core.Events;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Macros;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Request;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Web.Security;
using Umbraco.Core.Session;
namespace Umbraco.Web.Macros
{
@@ -29,10 +29,23 @@ namespace Umbraco.Web.Macros
private readonly IMacroService _macroService;
private readonly IIOHelper _ioHelper;
private readonly ICookieManager _cookieManager;
private readonly IUserService _userService;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IMemberUserKeyProvider _memberUserKeyProvider;
private readonly ISessionManager _sessionManager;
private readonly IRequestAccessor _requestAccessor;
public MacroRenderer(IProfilingLogger plogger, IUmbracoContextAccessor umbracoContextAccessor, IContentSection contentSection, ILocalizedTextService textService, AppCaches appCaches, IMacroService macroService, IUserService userService, IHttpContextAccessor httpContextAccessor, IIOHelper ioHelper, ICookieManager cookieManager)
public MacroRenderer(
IProfilingLogger plogger,
IUmbracoContextAccessor umbracoContextAccessor,
IContentSection contentSection,
ILocalizedTextService textService,
AppCaches appCaches,
IMacroService macroService,
IIOHelper ioHelper,
ICookieManager cookieManager,
IMemberUserKeyProvider memberUserKeyProvider,
ISessionManager sessionManager,
IRequestAccessor requestAccessor)
{
_plogger = plogger ?? throw new ArgumentNullException(nameof(plogger));
_umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
@@ -42,8 +55,9 @@ namespace Umbraco.Web.Macros
_macroService = macroService ?? throw new ArgumentNullException(nameof(macroService));
_ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper));
_cookieManager = cookieManager;
_userService = userService ?? throw new ArgumentNullException(nameof(userService));
_httpContextAccessor = httpContextAccessor;
_memberUserKeyProvider = memberUserKeyProvider;
_sessionManager = sessionManager;
_requestAccessor = requestAccessor;
}
#region MacroContent cache
@@ -63,12 +77,9 @@ namespace Umbraco.Web.Macros
{
object key = 0;
if (_httpContextAccessor.HttpContext?.User?.Identity?.IsAuthenticated ?? false)
if (_umbracoContextAccessor.UmbracoContext.Security.IsAuthenticated())
{
//ugh, membershipproviders :(
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
var member = MembershipProviderExtensions.GetCurrentUser(provider);
key = member?.ProviderUserKey ?? 0;
key = _memberUserKeyProvider.GetMemberProviderUserKey() ?? 0;
}
id.AppendFormat("m{0}-", key);
@@ -127,10 +138,8 @@ namespace Umbraco.Web.Macros
// do not cache if it should cache by member and there's not member
if (model.CacheByMember)
{
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
var member = MembershipProviderExtensions.GetCurrentUser(provider);
var key = member?.ProviderUserKey;
if (key == null) return;
var key = _memberUserKeyProvider.GetMemberProviderUserKey();
if (key is null) return;
}
// remember when we cache the content
@@ -364,8 +373,6 @@ namespace Umbraco.Web.Macros
return attributeValue;
}
var context = _httpContextAccessor.HttpContext;
foreach (var token in tokens)
{
var isToken = token.Length > 4 && token[0] == '[' && token[token.Length - 1] == ']' && validTypes.Contains(token[1]);
@@ -383,10 +390,10 @@ namespace Umbraco.Web.Macros
switch (type)
{
case '@':
attributeValue = context?.Request[name];
attributeValue = _requestAccessor.GetRequestValue(name);
break;
case '%':
attributeValue = context?.Session[name]?.ToString();
attributeValue = _sessionManager.GetSessionValue(name)?.ToString();
if (string.IsNullOrEmpty(attributeValue))
attributeValue = _cookieManager.GetCookieValue(name);
break;

View File

@@ -0,0 +1,17 @@
using Umbraco.Core.Security;
using Umbraco.Web.Security;
namespace Umbraco.Web.Macros
{
internal class MemberUserKeyProvider : IMemberUserKeyProvider
{
public object GetMemberProviderUserKey()
{
//ugh, membershipproviders :(
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
var member = MembershipProviderExtensions.GetCurrentUser(provider);
return member?.ProviderUserKey;
}
}
}

View File

@@ -1,5 +1,4 @@
using System.Linq;
using System.Web;
using System.Web.Security;
using Examine;
using Microsoft.AspNet.SignalR;
@@ -7,13 +6,11 @@ using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Cookie;
using Umbraco.Core.Dashboards;
using Umbraco.Core.Dictionary;
using Umbraco.Core.Events;
using Umbraco.Core.Hosting;
using Umbraco.Core.Install;
using Umbraco.Core.Migrations.PostMigrations;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors.ValueConverters;
using Umbraco.Core.Runtime;
@@ -49,6 +46,9 @@ using Current = Umbraco.Web.Composing.Current;
using Umbraco.Web.PropertyEditors;
using Umbraco.Examine;
using Umbraco.Core.Models;
using Umbraco.Core.Request;
using Umbraco.Core.Session;
using Umbraco.Web.AspNet;
using Umbraco.Web.Models;
namespace Umbraco.Web.Runtime
@@ -64,7 +64,14 @@ namespace Umbraco.Web.Runtime
composition.Register<UmbracoInjectedModule>();
composition.Register<IIpResolver, AspNetIpResolver>();
composition.Register<ISessionIdResolver, AspNetSessionIdResolver>();
composition.Register<AspNetSessionManager>(Lifetime.Singleton);
composition.Register<ISessionIdResolver>(factory => factory.GetInstance<AspNetSessionManager>(), Lifetime.Singleton);
composition.Register<ISessionManager>(factory => factory.GetInstance<AspNetSessionManager>(), Lifetime.Singleton);
composition.Register<IRequestAccessor, AspNetRequestAccessor>();
composition.Register<IHostingEnvironment, AspNetHostingEnvironment>();
composition.Register<IBackOfficeInfo, AspNetBackOfficeInfo>();
composition.Register<IPasswordHasher, AspNetPasswordHasher>();
@@ -73,6 +80,7 @@ namespace Umbraco.Web.Runtime
composition.RegisterUnique<IHttpContextAccessor, AspNetHttpContextAccessor>(); // required for hybrid accessors
composition.RegisterUnique<ICookieManager, AspNetCookieManager>();
composition.ComposeWebMappingProfiles();
//register the install components
@@ -85,6 +93,8 @@ namespace Umbraco.Web.Runtime
composition.Register(factory => Roles.Enabled ? Roles.Provider : new MembersRoleProvider(factory.GetInstance<IMemberService>()));
composition.Register<MembershipHelper>(Lifetime.Request);
composition.Register<IPublishedMemberCache>(factory => factory.GetInstance<IUmbracoContext>().PublishedSnapshot.Members);
composition.RegisterUnique<IMemberUserKeyProvider, MemberUserKeyProvider>();
composition.RegisterUnique<IPublicAccessChecker, PublicAccessChecker>();
// register accessors for cultures
composition.RegisterUnique<IDefaultCultureAccessor, DefaultCultureAccessor>();
@@ -115,6 +125,7 @@ namespace Umbraco.Web.Runtime
composition.RegisterUnique<ITemplateRenderer, TemplateRenderer>();
composition.RegisterUnique<IMacroRenderer, MacroRenderer>();
composition.RegisterUnique<IUmbracoComponentRenderer, UmbracoComponentRenderer>();
composition.RegisterUnique<HtmlLocalLinkParser>();
@@ -125,6 +136,7 @@ namespace Umbraco.Web.Runtime
// register the umbraco helper - this is Transient! very important!
// also, if not level.Run, we cannot really use the helper (during upgrade...)
// so inject a "void" helper (not exactly pretty but...)
if (composition.RuntimeState.Level == RuntimeLevel.Run)
if (composition.RuntimeState.Level == RuntimeLevel.Run)
composition.Register<UmbracoHelper>(factory =>
{

View File

@@ -0,0 +1,54 @@
using System;
using Umbraco.Core;
using Umbraco.Core.Services;
namespace Umbraco.Web.Security
{
public class PublicAccessChecker : IPublicAccessChecker
{
//TODO: This is lazy to avoid circular dependency. We don't care right now because all membership is going to be changed.
private readonly Lazy<MembershipHelper> _membershipHelper;
private readonly IPublicAccessService _publicAccessService;
private readonly IContentService _contentService;
public PublicAccessChecker(Lazy<MembershipHelper> membershipHelper, IPublicAccessService publicAccessService, IContentService contentService)
{
_membershipHelper = membershipHelper;
_publicAccessService = publicAccessService;
_contentService = contentService;
}
public PublicAccessStatus HasMemberAccessToContent(int publishedContentId)
{
var membershipHelper = _membershipHelper.Value;
if (membershipHelper.IsLoggedIn() == false)
{
return PublicAccessStatus.NotLoggedIn;
}
var username = membershipHelper.CurrentUserName;
var userRoles = membershipHelper.GetCurrentUserRoles();
if (_publicAccessService.HasAccess(publishedContentId, _contentService, username, userRoles) == false)
{
return PublicAccessStatus.AccessDenied;
}
var member = membershipHelper.GetCurrentMember();
if (member.HasProperty(Constants.Conventions.Member.IsApproved) == false)
{
return PublicAccessStatus.NotApproved;
}
if (member.HasProperty(Constants.Conventions.Member.IsLockedOut) &&
member.Value<bool>(Constants.Conventions.Member.IsApproved))
{
return PublicAccessStatus.LockedOut;
}
return PublicAccessStatus.AccessAccepted;
}
}
}

View File

@@ -134,6 +134,8 @@
<Compile Include="AppBuilderExtensions.cs" />
<Compile Include="AreaRegistrationContextExtensions.cs" />
<Compile Include="AspNet\AspNetHostingEnvironment.cs" />
<Compile Include="AspNet\AspNetRequestAccessor.cs" />
<Compile Include="AspNet\AspNetSessionManager.cs" />
<Compile Include="AspNet\FrameworkMarchal.cs" />
<Compile Include="Cache\WebCachingAppCache.cs" />
<Compile Include="Compose\AuditEventsComponent.cs" />
@@ -166,6 +168,8 @@
<Compile Include="Logging\WebProfilerComponent.cs" />
<Compile Include="Logging\WebProfilerComposer.cs" />
<Compile Include="Logging\WebProfilerProvider.cs" />
<Compile Include="Macros\MacroRenderer.cs" />
<Compile Include="Macros\MemberUserKeyProvider.cs" />
<Compile Include="Models\Identity\BackOfficeIdentityUser.cs" />
<Compile Include="Models\Identity\IdentityMapDefinition.cs" />
<Compile Include="Models\Identity\IdentityUser.cs" />
@@ -185,7 +189,6 @@
<Compile Include="AspNet\AspNetHttpContextAccessor.cs" />
<Compile Include="AspNet\AspNetIpResolver.cs" />
<Compile Include="AspNet\AspNetPasswordHasher.cs" />
<Compile Include="AspNet\AspNetSessionIdResolver.cs" />
<Compile Include="Profiling\WebProfilingController.cs" />
<Compile Include="RoutableDocumentFilter.cs" />
<Compile Include="Runtime\AspNetUmbracoBootPermissionChecker.cs" />
@@ -200,6 +203,7 @@
<Compile Include="Security\MembershipProviderBase.cs" />
<Compile Include="Security\MembershipProviderExtensions.cs" />
<Compile Include="Security\PasswordSecurity.cs" />
<Compile Include="Security\PublicAccessChecker.cs" />
<Compile Include="Security\UmbracoBackOfficeIdentity.cs" />
<Compile Include="Security\UmbracoEmailMessage.cs" />
<Compile Include="Security\UmbracoMembershipProviderBase.cs" />
@@ -214,7 +218,6 @@
<Compile Include="ViewDataExtensions.cs" />
<Compile Include="WebApi\Filters\AdminUsersAuthorizeAttribute.cs" />
<Compile Include="WebApi\Filters\OnlyLocalRequestsAttribute.cs" />
<Compile Include="Routing\RedirectTrackingComposer.cs" />
<Compile Include="Runtime\WebInitialComposer.cs" />
<Compile Include="Security\ActiveDirectoryBackOfficeUserPasswordChecker.cs" />
<Compile Include="Security\BackOfficeClaimsIdentityFactory.cs" />
@@ -274,7 +277,6 @@
<Compile Include="Editors\MemberGroupController.cs" />
<Compile Include="Composing\CompositionExtensions\Controllers.cs" />
<Compile Include="HealthCheck\HealthCheckController.cs" />
<Compile Include="Macros\MacroRenderer.cs" />
<Compile Include="HtmlHelperBackOfficeExtensions.cs" />
<Compile Include="Composing\ModuleInjector.cs" />
<Compile Include="Mvc\FilteredControllerFactoryCollection.cs" />
@@ -323,7 +325,6 @@
<Compile Include="Mvc\UmbracoRequireHttpsAttribute.cs" />
<Compile Include="Mvc\ValidateMvcAngularAntiForgeryTokenAttribute.cs" />
<Compile Include="OwinMiddlewareConfiguredEventArgs.cs" />
<Compile Include="Routing\RedirectTrackingComponent.cs" />
<Compile Include="Editors\RedirectUrlManagementController.cs" />
<Compile Include="DefaultEventMessagesFactory.cs" />
<Compile Include="Security\ExternalSignInAutoLinkOptions.cs" />
@@ -529,8 +530,6 @@
<Compile Include="Mvc\PluginControllerMetadata.cs" />
<Compile Include="Mvc\UmbracoPageResult.cs" />
<Compile Include="RouteCollectionExtensions.cs" />
<Compile Include="Routing\ContentFinderByPageIdQuery.cs" />
<Compile Include="Routing\PublishedRouter.cs" />
<Compile Include="Compose\DatabaseServerRegistrarAndMessengerComponent.cs" />
<Compile Include="Templates\TemplateRenderer.cs" />
<Compile Include="Trees\PartialViewMacrosTreeController.cs" />
@@ -556,7 +555,6 @@
<Compile Include="WebApi\Filters\UmbracoUserTimeoutFilterAttribute.cs" />
<Compile Include="Runtime\WebRuntime.cs" />
<Compile Include="Mvc\ControllerExtensions.cs" />
<Compile Include="Routing\ContentFinderByIdPath.cs" />
<Compile Include="TypeLoaderExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>

View File

@@ -11,6 +11,7 @@ using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Logging.Serilog;
using Umbraco.Web.AspNet;
using Umbraco.Web.Hosting;
using Current = Umbraco.Web.Composing.Current;
@@ -39,7 +40,7 @@ namespace Umbraco.Web
var ioHelper = new IOHelper(hostingEnvironment);
var configs = configFactory.Create(ioHelper);
var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, new AspNetSessionIdResolver(), () => _factory?.GetInstance<IRequestCache>(), coreDebug, ioHelper, new FrameworkMarchal());
var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, new AspNetSessionManager(), () => _factory?.GetInstance<IRequestCache>(), coreDebug, ioHelper, new FrameworkMarchal());
var backOfficeInfo = new AspNetBackOfficeInfo(configs.Global(), ioHelper, configs.Settings(), logger);
var profiler = new LogProfiler(logger);
Umbraco.Composing.Current.Initialize(logger, configs, ioHelper, hostingEnvironment, backOfficeInfo, profiler);