diff --git a/src/Umbraco.Core/Cookie/ICookieManager.cs b/src/Umbraco.Core/Cookie/ICookieManager.cs index af0ee7b1f6..0eced07b37 100644 --- a/src/Umbraco.Core/Cookie/ICookieManager.cs +++ b/src/Umbraco.Core/Cookie/ICookieManager.cs @@ -7,4 +7,5 @@ namespace Umbraco.Core.Cookie void SetCookieValue(string cookieName, string value); bool HasCookie(string cookieName); } + } diff --git a/src/Umbraco.Core/Request/IRequestAccessor.cs b/src/Umbraco.Core/Request/IRequestAccessor.cs new file mode 100644 index 0000000000..63a8de6b1e --- /dev/null +++ b/src/Umbraco.Core/Request/IRequestAccessor.cs @@ -0,0 +1,13 @@ +using System; +using Umbraco.Web.Routing; + +namespace Umbraco.Core.Request +{ + public interface IRequestAccessor + { + string GetRequestValue(string name); + string GetQueryStringValue(string culture); + event EventHandler EndRequest; + event EventHandler RouteAttempt; + } +} diff --git a/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs b/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs similarity index 88% rename from src/Umbraco.Web/Routing/ContentFinderByIdPath.cs rename to src/Umbraco.Core/Routing/ContentFinderByIdPath.cs index bf7d5ef7c4..c4bfd5a697 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs @@ -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; } /// @@ -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; diff --git a/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs b/src/Umbraco.Core/Routing/ContentFinderByPageIdQuery.cs similarity index 69% rename from src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs rename to src/Umbraco.Core/Routing/ContentFinderByPageIdQuery.cs index fb79e13dbc..6a9adda5f8 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByPageIdQuery.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Web.Routing +using Umbraco.Core.Request; + +namespace Umbraco.Web.Routing { /// /// This looks up a document by checking for the umbPageId of a request/query string @@ -9,17 +11,17 @@ /// 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); diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Core/Routing/PublishedRouter.cs similarity index 88% rename from src/Umbraco.Web/Routing/PublishedRouter.cs rename to src/Umbraco.Core/Routing/PublishedRouter.cs index 1a6048e4ec..f521c3a95a 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Core/Routing/PublishedRouter.cs @@ -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; /// /// Initializes a new instance of the 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; } /// @@ -358,7 +369,7 @@ namespace Umbraco.Web.Routing /// public ITemplate GetTemplate(string alias) { - return _services.FileService.GetTemplate(alias); + return _fileService.GetTemplate(alias); } /// @@ -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(Constants.Conventions.Content.InternalRedirectId); + var udiInternalRedirectId = request.PublishedContent.Value(_publishedValueFallback, Constants.Conventions.Content.InternalRedirectId); if (udiInternalRedirectId != null) { // try and get the redirect node from a UDI Guid @@ -555,58 +566,34 @@ namespace Umbraco.Web.Routing var path = request.PublishedContent.Path; - var publicAccessAttempt = _services.PublicAccessService.IsProtected(path); + var publicAccessAttempt = _publicAccessService.IsProtected(path); if (publicAccessAttempt) { _logger.Debug("EnsurePublishedContentAccess: Page is protected, check for access"); - var membershipHelper = Current.Factory.GetInstance(); - - if (membershipHelper.IsLoggedIn() == false) + var status = _publicAccessChecker.HasMemberAccessToContent(request.PublishedContent.Id); + switch (status) { - _logger.Debug("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("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(Constants.Conventions.Member.IsApproved); - - if (member.HasProperty(Constants.Conventions.Member.IsLockedOut) == false) - memberIsActive = member.Value(Constants.Conventions.Member.IsLockedOut) == false; - } - - if (memberIsActive == false) - { - _logger.Debug( - "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("EnsurePublishedContentAccess: Not logged in, redirect to login page"); + SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.LoginNodeId); + break; + case PublicAccessStatus.AccessDenied: + _logger.Debug("EnsurePublishedContentAccess: Current member has not access, redirect to error page"); + SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId); + break; + case PublicAccessStatus.LockedOut: + _logger.Debug("Current member is locked out, redirect to error page"); + SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId); + break; + case PublicAccessStatus.NotApproved: + _logger.Debug("Current member is unapproved, redirect to error page"); + SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId); + break; + case PublicAccessStatus.AccessAccepted: _logger.Debug("Current member has access"); - } + break; } } else @@ -615,6 +602,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); + } + /// /// Finds a template for the current node, if any. /// @@ -637,7 +630,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 +667,15 @@ namespace Umbraco.Web.Routing _logger.Debug("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 +729,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("GetTemplateModel: Got template id={TemplateId} alias={TemplateAlias}", template.Id, template.Alias); @@ -750,7 +748,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 +757,7 @@ namespace Umbraco.Web.Routing else { // might be a UDI instead of an int Id - var redirectUdi = request.PublishedContent.Value(Constants.Conventions.Content.Redirect); + var redirectUdi = request.PublishedContent.Value(_publishedValueFallback, Constants.Conventions.Content.Redirect); if (redirectUdi != null) redirectUrl = _publishedUrlProvider.GetUrl(redirectUdi.Guid); } diff --git a/src/Umbraco.Core/Security/IMemberUserKeyProvider.cs b/src/Umbraco.Core/Security/IMemberUserKeyProvider.cs new file mode 100644 index 0000000000..439e7a82b8 --- /dev/null +++ b/src/Umbraco.Core/Security/IMemberUserKeyProvider.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Security +{ + public interface IMemberUserKeyProvider + { + object GetMemberProviderUserKey(); + } +} diff --git a/src/Umbraco.Core/Security/IPublicAccessChecker.cs b/src/Umbraco.Core/Security/IPublicAccessChecker.cs new file mode 100644 index 0000000000..a47186394e --- /dev/null +++ b/src/Umbraco.Core/Security/IPublicAccessChecker.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Web.Security +{ + public interface IPublicAccessChecker + { + PublicAccessStatus HasMemberAccessToContent(int publishedContentId); + } +} diff --git a/src/Umbraco.Core/Security/PublicAccessStatus.cs b/src/Umbraco.Core/Security/PublicAccessStatus.cs new file mode 100644 index 0000000000..57df423749 --- /dev/null +++ b/src/Umbraco.Core/Security/PublicAccessStatus.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Web.Security +{ + public enum PublicAccessStatus + { + NotLoggedIn, + AccessDenied, + NotApproved, + LockedOut, + AccessAccepted + } +} diff --git a/src/Umbraco.Core/Session/ISessionManager.cs b/src/Umbraco.Core/Session/ISessionManager.cs new file mode 100644 index 0000000000..f3a47202ee --- /dev/null +++ b/src/Umbraco.Core/Session/ISessionManager.cs @@ -0,0 +1,8 @@ +namespace Umbraco.Core.Session +{ + public interface ISessionManager + { + object GetSessionValue(string sessionName); + void SetSessionValue(string sessionName, object value); + } +} diff --git a/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs index d63478ef96..d8ec82818d 100644 --- a/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs @@ -3,8 +3,10 @@ namespace Umbraco.Core.Sync /// /// An implementation that works by storing messages in the database. /// - public interface IBatchedDatabaseServerMessenger : IServerMessenger + public interface IBatchedDatabaseServerMessenger : IDatabaseServerMessenger { void FlushBatch(); + DatabaseServerMessengerOptions Options { get; } + void Startup(); } } diff --git a/src/Umbraco.Core/Sync/IDatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/IDatabaseServerMessenger.cs new file mode 100644 index 0000000000..a49cfdd023 --- /dev/null +++ b/src/Umbraco.Core/Sync/IDatabaseServerMessenger.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Sync +{ + public interface IDatabaseServerMessenger: IServerMessenger + { + void Sync(); + } +} diff --git a/src/Umbraco.Web/WebApi/UmbracoApiControllerTypeCollection.cs b/src/Umbraco.Core/UmbracoApiControllerTypeCollection.cs similarity index 100% rename from src/Umbraco.Web/WebApi/UmbracoApiControllerTypeCollection.cs rename to src/Umbraco.Core/UmbracoApiControllerTypeCollection.cs diff --git a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs b/src/Umbraco.Infrastructure/BatchedDatabaseServerMessenger.cs similarity index 87% rename from src/Umbraco.Web/BatchedDatabaseServerMessenger.cs rename to src/Umbraco.Infrastructure/BatchedDatabaseServerMessenger.cs index a62aad3e5d..78b9589a2e 100644 --- a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs +++ b/src/Umbraco.Infrastructure/BatchedDatabaseServerMessenger.cs @@ -1,21 +1,17 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Web; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; using Umbraco.Core.Sync; using Umbraco.Web.Routing; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Scoping; -using Umbraco.Web.Composing; -using System.ComponentModel; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; +using Umbraco.Core.Request; namespace Umbraco.Web { @@ -29,19 +25,30 @@ namespace Umbraco.Web { private readonly IUmbracoDatabaseFactory _databaseFactory; private readonly IRequestCache _requestCache; + private readonly IRequestAccessor _requestAccessor; public BatchedDatabaseServerMessenger( - IRuntimeState runtime, IUmbracoDatabaseFactory databaseFactory, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, DatabaseServerMessengerOptions options, IHostingEnvironment hostingEnvironment, CacheRefresherCollection cacheRefreshers, IRequestCache requestCache) + IRuntimeState runtime, + IUmbracoDatabaseFactory databaseFactory, + IScopeProvider scopeProvider, + ISqlContext sqlContext, + IProfilingLogger proflog, + DatabaseServerMessengerOptions options, + IHostingEnvironment hostingEnvironment, + CacheRefresherCollection cacheRefreshers, + IRequestCache requestCache, + IRequestAccessor requestAccessor) : base(runtime, scopeProvider, sqlContext, proflog, true, options, hostingEnvironment, cacheRefreshers) { _databaseFactory = databaseFactory; _requestCache = requestCache; + _requestAccessor = requestAccessor; } // invoked by DatabaseServerRegistrarAndMessengerComponent - internal void Startup() + public void Startup() { - UmbracoModule.EndRequest += UmbracoModule_EndRequest; + _requestAccessor.EndRequest += UmbracoModule_EndRequest; if (_databaseFactory.CanConnect == false) { @@ -104,7 +111,7 @@ namespace Umbraco.Web protected ICollection GetBatch(bool create) { - var key = typeof (BatchedDatabaseServerMessenger).Name; + var key = nameof(BatchedDatabaseServerMessenger); if (!_requestCache.IsAvailable) return null; diff --git a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Infrastructure/Compose/DatabaseServerRegistrarAndMessengerComponent.cs similarity index 91% rename from src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs rename to src/Umbraco.Infrastructure/Compose/DatabaseServerRegistrarAndMessengerComponent.cs index 1fa455ed8a..2a24e6f318 100644 --- a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/DatabaseServerRegistrarAndMessengerComponent.cs @@ -4,6 +4,7 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Hosting; using Umbraco.Core.Logging; +using Umbraco.Core.Request; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Sync; @@ -82,7 +83,7 @@ namespace Umbraco.Web.Compose { private object _locker = new object(); private readonly DatabaseServerRegistrar _registrar; - private readonly BatchedDatabaseServerMessenger _messenger; + private readonly IBatchedDatabaseServerMessenger _messenger; private readonly IRuntimeState _runtime; private readonly ILogger _logger; private readonly IServerRegistrationService _registrationService; @@ -91,13 +92,23 @@ namespace Umbraco.Web.Compose private bool _started; private IBackgroundTask[] _tasks; private IndexRebuilder _indexRebuilder; + private readonly IRequestAccessor _requestAccessor; - public DatabaseServerRegistrarAndMessengerComponent(IRuntimeState runtime, IServerRegistrar serverRegistrar, IServerMessenger serverMessenger, IServerRegistrationService registrationService, ILogger logger, IHostingEnvironment hostingEnvironment, IndexRebuilder indexRebuilder) + public DatabaseServerRegistrarAndMessengerComponent( + IRuntimeState runtime, + IServerRegistrar serverRegistrar, + IServerMessenger serverMessenger, + IServerRegistrationService registrationService, + ILogger logger, + IHostingEnvironment hostingEnvironment, + IndexRebuilder indexRebuilder, + IRequestAccessor requestAccessor) { _runtime = runtime; _logger = logger; _registrationService = registrationService; _indexRebuilder = indexRebuilder; + _requestAccessor = requestAccessor; // create task runner for DatabaseServerRegistrar _registrar = serverRegistrar as DatabaseServerRegistrar; @@ -108,7 +119,7 @@ namespace Umbraco.Web.Compose } // create task runner for BatchedDatabaseServerMessenger - _messenger = serverMessenger as BatchedDatabaseServerMessenger; + _messenger = serverMessenger as IBatchedDatabaseServerMessenger; if (_messenger != null) { _processTaskRunner = new BackgroundTaskRunner("ServerInstProcess", @@ -120,7 +131,7 @@ namespace Umbraco.Web.Compose { //We will start the whole process when a successful request is made if (_registrar != null || _messenger != null) - UmbracoModule.RouteAttempt += RegisterBackgroundTasksOnce; + _requestAccessor.RouteAttempt += RegisterBackgroundTasksOnce; // must come last, as it references some _variables _messenger?.Startup(); @@ -137,7 +148,7 @@ namespace Umbraco.Web.Compose /// /// We require this because: /// - ApplicationContext.UmbracoApplicationUrl is initialized by UmbracoModule in BeginRequest - /// - RegisterServer is called on UmbracoModule.RouteAttempt which is triggered in ProcessRequest + /// - RegisterServer is called on _requestAccessor.RouteAttempt which is triggered in ProcessRequest /// we are safe, UmbracoApplicationUrl has been initialized /// private void RegisterBackgroundTasksOnce(object sender, RoutableAttemptEventArgs e) @@ -146,7 +157,7 @@ namespace Umbraco.Web.Compose { case EnsureRoutableOutcome.IsRoutable: case EnsureRoutableOutcome.NotDocumentRequest: - UmbracoModule.RouteAttempt -= RegisterBackgroundTasksOnce; + _requestAccessor.RouteAttempt -= RegisterBackgroundTasksOnce; RegisterBackgroundTasks(); break; } @@ -196,11 +207,11 @@ namespace Umbraco.Web.Compose private class InstructionProcessTask : RecurringTaskBase { - private readonly DatabaseServerMessenger _messenger; + private readonly IDatabaseServerMessenger _messenger; private readonly ILogger _logger; public InstructionProcessTask(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - DatabaseServerMessenger messenger, ILogger logger) + IDatabaseServerMessenger messenger, ILogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _messenger = messenger; diff --git a/src/Umbraco.Infrastructure/Migrations/PostMigrations/ClearCsrfCookies.cs b/src/Umbraco.Infrastructure/Migrations/PostMigrations/ClearCsrfCookies.cs index ab9c946e05..4f176c797f 100644 --- a/src/Umbraco.Infrastructure/Migrations/PostMigrations/ClearCsrfCookies.cs +++ b/src/Umbraco.Infrastructure/Migrations/PostMigrations/ClearCsrfCookies.cs @@ -2,7 +2,6 @@ using Umbraco.Core.Cookie; using Umbraco.Core.Migrations; - namespace Umbraco.Web.Migrations.PostMigrations { /// diff --git a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs similarity index 93% rename from src/Umbraco.Web/Routing/RedirectTrackingComponent.cs rename to src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs index dffb956b1a..0eae54bf7d 100644 --- a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs @@ -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; diff --git a/src/Umbraco.Web/Routing/RedirectTrackingComposer.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComposer.cs similarity index 100% rename from src/Umbraco.Web/Routing/RedirectTrackingComposer.cs rename to src/Umbraco.Infrastructure/Routing/RedirectTrackingComposer.cs diff --git a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs b/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs similarity index 96% rename from src/Umbraco.Web/Scheduling/SchedulerComponent.cs rename to src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs index 061f42b9ba..6e5d0ee81e 100644 --- a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Core.Request; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Sync; @@ -37,6 +38,7 @@ namespace Umbraco.Web.Scheduling private readonly IUmbracoSettingsSection _umbracoSettingsSection; private readonly IIOHelper _ioHelper; private readonly IServerMessenger _serverMessenger; + private readonly IRequestAccessor _requestAccessor; private BackgroundTaskRunner _keepAliveRunner; private BackgroundTaskRunner _publishingRunner; @@ -54,7 +56,7 @@ namespace Umbraco.Web.Scheduling HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, IScopeProvider scopeProvider, IUmbracoContextFactory umbracoContextFactory, IProfilingLogger logger, IHostingEnvironment hostingEnvironment, IHealthChecks healthChecksConfig, - IUmbracoSettingsSection umbracoSettingsSection, IIOHelper ioHelper, IServerMessenger serverMessenger) + IUmbracoSettingsSection umbracoSettingsSection, IIOHelper ioHelper, IServerMessenger serverMessenger, IRequestAccessor requestAccessor) { _runtime = runtime; _contentService = contentService; @@ -70,6 +72,7 @@ namespace Umbraco.Web.Scheduling _umbracoSettingsSection = umbracoSettingsSection ?? throw new ArgumentNullException(nameof(umbracoSettingsSection)); _ioHelper = ioHelper; _serverMessenger = serverMessenger; + _requestAccessor = requestAccessor; } public void Initialize() @@ -83,7 +86,7 @@ namespace Umbraco.Web.Scheduling _healthCheckRunner = new BackgroundTaskRunner("HealthCheckNotifier", _logger, _hostingEnvironment); // we will start the whole process when a successful request is made - UmbracoModule.RouteAttempt += RegisterBackgroundTasksOnce; + _requestAccessor.RouteAttempt += RegisterBackgroundTasksOnce; } public void Terminate() @@ -97,7 +100,7 @@ namespace Umbraco.Web.Scheduling { case EnsureRoutableOutcome.IsRoutable: case EnsureRoutableOutcome.NotDocumentRequest: - UmbracoModule.RouteAttempt -= RegisterBackgroundTasksOnce; + _requestAccessor.RouteAttempt -= RegisterBackgroundTasksOnce; RegisterBackgroundTasks(); break; } diff --git a/src/Umbraco.Web/Scheduling/SchedulerComposer.cs b/src/Umbraco.Infrastructure/Scheduling/SchedulerComposer.cs similarity index 100% rename from src/Umbraco.Web/Scheduling/SchedulerComposer.cs rename to src/Umbraco.Infrastructure/Scheduling/SchedulerComposer.cs diff --git a/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs index 948304e4e4..5a46a37d43 100644 --- a/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs @@ -26,7 +26,7 @@ namespace Umbraco.Core.Sync // but only processes instructions coming from remote servers, // thus ensuring that instructions run only once // - public class DatabaseServerMessenger : ServerMessengerBase + public class DatabaseServerMessenger : ServerMessengerBase, IDatabaseServerMessenger { private readonly IRuntimeState _runtime; private readonly ManualResetEvent _syncIdle; @@ -126,10 +126,6 @@ namespace Umbraco.Core.Sync const int weight = 10; - //TODO Why do we have interface if we expect to be exact type!!!? - // if (!(_runtime is RuntimeState runtime)) - // throw new NotSupportedException($"Unsupported IRuntimeState implementation {_runtime.GetType().FullName}, expecting {typeof(RuntimeState).FullName}."); - var registered = _runtime.MainDom.Register( () => { diff --git a/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs b/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs index 3b47cabbaf..b65254b181 100644 --- a/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs +++ b/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs @@ -4,7 +4,6 @@ using System.Linq; using Newtonsoft.Json; using Umbraco.Composing; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; namespace Umbraco.Core.Sync diff --git a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs index 77200be86e..3a504ad4e2 100644 --- a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Linq; using System.Reflection; using System.Web; using Moq; @@ -20,14 +21,17 @@ namespace Umbraco.Tests.Cache public class DeepCloneAppCacheTests : RuntimeAppCacheTests { private DeepCloneAppCache _provider; + private ObjectCacheAppCache _memberCache; - protected override int GetTotalItemCount => HttpRuntime.Cache.Count; + protected override int GetTotalItemCount => _memberCache.MemoryCache.Count(); public override void Setup() { base.Setup(); var typeFinder = new TypeFinder(Mock.Of()); - _provider = new DeepCloneAppCache(new WebCachingAppCache(HttpRuntime.Cache, typeFinder)); + _memberCache = new ObjectCacheAppCache(typeFinder); + + _provider = new DeepCloneAppCache(_memberCache); } internal override IAppCache AppCache => _provider; diff --git a/src/Umbraco.Tests/Cache/WebCachingAppCacheTests.cs b/src/Umbraco.Tests/Cache/WebCachingAppCacheTests.cs deleted file mode 100644 index 02986e2f78..0000000000 --- a/src/Umbraco.Tests/Cache/WebCachingAppCacheTests.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Diagnostics; -using System.Web; -using Moq; -using NUnit.Framework; -using Umbraco.Core.Cache; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; -using Umbraco.Web.Cache; - -namespace Umbraco.Tests.Cache -{ - [TestFixture] - public class WebCachingAppCacheTests : RuntimeAppCacheTests - { - private WebCachingAppCache _appCache; - - protected override int GetTotalItemCount => HttpRuntime.Cache.Count; - - public override void Setup() - { - base.Setup(); - var typeFinder = new TypeFinder(Mock.Of()); - _appCache = new WebCachingAppCache(HttpRuntime.Cache, typeFinder); - } - - internal override IAppCache AppCache => _appCache; - - internal override IAppPolicyCache AppPolicyCache => _appCache; - - [Test] - public void DoesNotCacheExceptions() - { - string value; - Assert.Throws(() => { value = (string)_appCache.Get("key", () => GetValue(1)); }); - Assert.Throws(() => { value = (string)_appCache.Get("key", () => GetValue(2)); }); - - // does not throw - value = (string)_appCache.Get("key", () => GetValue(3)); - Assert.AreEqual("succ3", value); - - // cache - value = (string)_appCache.Get("key", () => GetValue(4)); - Assert.AreEqual("succ3", value); - } - - private static string GetValue(int i) - { - Debug.Print("get" + i); - if (i < 3) - throw new Exception("fail"); - return "succ" + i; - } - } -} diff --git a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs index 875117afbc..227a7c26e3 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs @@ -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().WebRouting, Logger, HttpContextAccessor); + var lookup = new ContentFinderByIdPath(Factory.GetInstance().WebRouting, Logger, Factory.GetInstance()); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs index 20bbeb92d4..d18353eb87 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs @@ -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(); - mockHttpContextAccessor.Setup(x => x.HttpContext).Returns(httpContext); - var lookup = new ContentFinderByPageIdQuery(mockHttpContextAccessor.Object); + var mockRequestAccessor = new Mock(); + 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); diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index 505a66e322..48436d6690 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -49,8 +49,8 @@ namespace Umbraco.Tests.Routing public class TestRuntime : WebRuntime { - public TestRuntime(UmbracoApplicationBase umbracoApplication, Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) - : base(umbracoApplication, configs, umbracoVersion, ioHelper, Mock.Of(), Mock.Of(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom) + public TestRuntime(Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) + : base(configs, umbracoVersion, ioHelper, Mock.Of(), Mock.Of(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom) { } diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index e8b4a993c4..b09620103b 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -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()), new TestLastChanceFinder(), new TestVariationContextAccessor(), - container?.TryGetInstance() ?? ServiceContext.CreatePartial(), new ProfilingLogger(Mock.Of(), Mock.Of()), container?.TryGetInstance() ?? Current.Factory.GetInstance(), - Mock.Of(), - Mock.Of() - ); + Mock.Of(), + Mock.Of(), + container?.GetInstance() ?? Current.Factory.GetInstance(), + container?.GetInstance()?? Current.Factory.GetInstance(), + container?.GetInstance()?? Current.Factory.GetInstance(), + container?.GetInstance() ?? Current.Factory.GetInstance(), + container?.GetInstance() ?? Current.Factory.GetInstance() + ); } } } diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index c87b92f1c9..de0db554f3 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -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(); Composition.RegisterUnique(TestHelper.ShortStringHelper); + Composition.RegisterUnique(); + Composition.RegisterUnique(); + + + var memberService = Mock.Of(); + var memberTypeService = Mock.Of(); + var membershipProvider = new MembersMembershipProvider(memberService, memberTypeService, Mock.Of(), TestHelper.GetHostingEnvironment(), TestHelper.GetIpResolver()); + var membershipHelper = new MembershipHelper(Mock.Of(), Mock.Of(), membershipProvider, Mock.Of(), memberService, memberTypeService, Mock.Of(), AppCaches.Disabled, logger, ShortStringHelper, Mock.Of()); + + Composition.RegisterUnique(membershipHelper); + + TestObjects = new TestObjects(register); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 9815c94728..56d36981b3 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -322,7 +322,6 @@ - diff --git a/src/Umbraco.Web.UI/config/imageprocessor/processing.config b/src/Umbraco.Web.UI/config/imageprocessor/processing.config index 34c9fd96c4..dddcddb0bd 100644 --- a/src/Umbraco.Web.UI/config/imageprocessor/processing.config +++ b/src/Umbraco.Web.UI/config/imageprocessor/processing.config @@ -1,38 +1,10 @@  - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -63,4 +35,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Web/AspNet/AspNetRequestAccessor.cs b/src/Umbraco.Web/AspNet/AspNetRequestAccessor.cs new file mode 100644 index 0000000000..72bde75dc8 --- /dev/null +++ b/src/Umbraco.Web/AspNet/AspNetRequestAccessor.cs @@ -0,0 +1,43 @@ +using System; +using Umbraco.Core.Request; +using Umbraco.Web.Routing; + +namespace Umbraco.Web.AspNet +{ + public class AspNetRequestAccessor : IRequestAccessor + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public AspNetRequestAccessor(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + + UmbracoModule.EndRequest += OnEndRequest; + UmbracoModule.RouteAttempt += OnRouteAttempt; + } + + + + public string GetRequestValue(string name) + { + return _httpContextAccessor.GetRequiredHttpContext().Request[name]; + } + + public string GetQueryStringValue(string name) + { + return _httpContextAccessor.GetRequiredHttpContext().Request.QueryString[name]; + } + + private void OnEndRequest(object sender, UmbracoRequestEventArgs args) + { + EndRequest?.Invoke(sender, args); + } + + private void OnRouteAttempt(object sender, RoutableAttemptEventArgs args) + { + RouteAttempt?.Invoke(sender, args); + } + public event EventHandler EndRequest; + public event EventHandler RouteAttempt; + } +} diff --git a/src/Umbraco.Web/AspNet/AspNetSessionIdResolver.cs b/src/Umbraco.Web/AspNet/AspNetSessionIdResolver.cs deleted file mode 100644 index 1b22ebaa60..0000000000 --- a/src/Umbraco.Web/AspNet/AspNetSessionIdResolver.cs +++ /dev/null @@ -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; - } -} diff --git a/src/Umbraco.Web/AspNet/AspNetSessionManager.cs b/src/Umbraco.Web/AspNet/AspNetSessionManager.cs new file mode 100644 index 0000000000..bf7b1c05c3 --- /dev/null +++ b/src/Umbraco.Web/AspNet/AspNetSessionManager.cs @@ -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; + } +} diff --git a/src/Umbraco.Web/Cache/WebCachingAppCache.cs b/src/Umbraco.Web/Cache/WebCachingAppCache.cs deleted file mode 100644 index 087d393d3c..0000000000 --- a/src/Umbraco.Web/Cache/WebCachingAppCache.cs +++ /dev/null @@ -1,213 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Web.Caching; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Composing; - -namespace Umbraco.Web.Cache -{ - /// - /// Implements on top of a . - /// - /// The underlying cache is expected to be HttpRuntime.Cache. - internal class WebCachingAppCache : FastDictionaryAppCacheBase, IAppPolicyCache - { - // locker object that supports upgradeable read locking - // does not need to support recursion if we implement the cache correctly and ensure - // that methods cannot be reentrant, ie we do NOT create values while holding a lock. - private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); - - private readonly System.Web.Caching.Cache _cache; - - /// - /// Initializes a new instance of the class. - /// - public WebCachingAppCache(System.Web.Caching.Cache cache, ITypeFinder typeFinder) : base(typeFinder) - { - _cache = cache; - } - - /// - public override object Get(string key, Func factory) - { - return Get(key, factory, null, dependentFiles: null); - } - - /// - public object Get(string key, Func factory, TimeSpan? timeout, bool isSliding = false, string[] dependentFiles = null) - { - CacheDependency dependency = null; - if (dependentFiles != null && dependentFiles.Any()) - { - dependency = new CacheDependency(dependentFiles); - } - return GetImpl(key, factory, timeout, isSliding, dependency); - } - - /// - public void Insert(string key, Func factory, TimeSpan? timeout = null, bool isSliding = false, string[] dependentFiles = null) - { - CacheDependency dependency = null; - if (dependentFiles != null && dependentFiles.Any()) - { - dependency = new CacheDependency(dependentFiles); - } - InsertImpl(key, factory, timeout, isSliding, dependency); - } - - #region Dictionary - - protected override IEnumerable GetDictionaryEntries() - { - const string prefix = CacheItemPrefix + "-"; - return _cache.Cast() - .Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix)); - } - - protected override void RemoveEntry(string key) - { - _cache.Remove(key); - } - - protected override object GetEntry(string key) - { - return _cache.Get(key); - } - - #endregion - - #region Lock - - protected override void EnterReadLock() - { - _locker.EnterReadLock(); - } - - protected override void EnterWriteLock() - { - _locker.EnterWriteLock(); - } - - protected override void ExitReadLock() - { - if (_locker.IsReadLockHeld) - _locker.ExitReadLock(); - } - - protected override void ExitWriteLock() - { - if (_locker.IsWriteLockHeld) - _locker.ExitWriteLock(); - } - - #endregion - - private object GetImpl(string key, Func factory, TimeSpan? timeout, bool isSliding = false, CacheDependency dependency = null) - { - key = GetCacheKey(key); - - // NOTE - because we don't know what getCacheItem does, how long it will take and whether it will hang, - // getCacheItem should run OUTSIDE of the global application lock else we run into lock contention and - // nasty performance issues. - - // So.... we insert a Lazy in the cache while holding the global application lock, and then rely - // on the Lazy lock to ensure that getCacheItem runs once and everybody waits on it, while the global - // application lock has been released. - - // NOTE - // The Lazy value creation may produce a null value. - // Must make sure (for backward compatibility) that we pretend they are not in the cache. - // So if we find an entry in the cache that already has its value created and is null, - // pretend it was not there. If value is not already created, wait... and return null, that's - // what prior code did. - - // NOTE - // The Lazy value creation may throw. - - // So... the null value _will_ be in the cache but never returned - - Lazy result; - - // Fast! - // Only one thread can enter an UpgradeableReadLock at a time, but it does not prevent other - // threads to enter a ReadLock in the meantime -- only upgrading to WriteLock will prevent all - // reads. We first try with a normal ReadLock for maximum concurrency and take the penalty of - // having to re-lock in case there's no value. Would need to benchmark to figure out whether - // it's worth it, though... - try - { - _locker.EnterReadLock(); - result = _cache.Get(key) as Lazy; // null if key not found - } - finally - { - if (_locker.IsReadLockHeld) - _locker.ExitReadLock(); - } - var value = result == null ? null : SafeLazy.GetSafeLazyValue(result); - if (value != null) return value; - - using (var lck = new UpgradeableReadLock(_locker)) - { - result = _cache.Get(key) as Lazy; // null if key not found - - // cannot create value within the lock, so if result.IsValueCreated is false, just - // do nothing here - means that if creation throws, a race condition could cause - // more than one thread to reach the return statement below and throw - accepted. - - if (result == null || SafeLazy.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null - { - result = SafeLazy.GetSafeLazy(factory); - var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); - var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration); - - lck.UpgradeToWriteLock(); - //NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update! - _cache.Insert(key, result, dependency, absolute, sliding, CacheItemPriority.Normal, null); - } - } - - // using GetSafeLazy and GetSafeLazyValue ensures that we don't cache - // exceptions (but try again and again) and silently eat them - however at - // some point we have to report them - so need to re-throw here - - // this does not throw anymore - //return result.Value; - - value = result.Value; // will not throw (safe lazy) - if (value is SafeLazy.ExceptionHolder eh) eh.Exception.Throw(); // throw once! - return value; - } - - private void InsertImpl(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheDependency dependency = null) - { - // NOTE - here also we must insert a Lazy but we can evaluate it right now - // and make sure we don't store a null value. - - var result = SafeLazy.GetSafeLazy(getCacheItem); - var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache - if (value == null) return; // do not store null values (backward compat) - - cacheKey = GetCacheKey(cacheKey); - - var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); - var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration); - - try - { - _locker.EnterWriteLock(); - //NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update! - _cache.Insert(cacheKey, result, dependency, absolute, sliding, CacheItemPriority.Normal, null); - } - finally - { - if (_locker.IsWriteLockHeld) - _locker.ExitWriteLock(); - } - } - } -} diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index 796a3a0c4a..f8b121f60c 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -96,7 +96,7 @@ namespace Umbraco.Web } if (cacheByMember) { - var helper = Current.Factory.GetInstance(); + var helper = Current.MembershipHelper; var currentMember = helper.GetCurrentMember(); cacheKey.AppendFormat("m{0}-", currentMember?.Id ?? 0); } diff --git a/src/Umbraco.Web/Macros/MacroRenderer.cs b/src/Umbraco.Web/Macros/MacroRenderer.cs old mode 100755 new mode 100644 index 9990644db8..253013d8c4 --- a/src/Umbraco.Web/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web/Macros/MacroRenderer.cs @@ -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,25 @@ namespace Umbraco.Web.Macros private readonly IMacroService _macroService; private readonly IIOHelper _ioHelper; private readonly ICookieManager _cookieManager; - private readonly IUserService _userService; + private readonly IMemberUserKeyProvider _memberUserKeyProvider; + private readonly ISessionManager _sessionManager; + private readonly IRequestAccessor _requestAccessor; private readonly IHttpContextAccessor _httpContextAccessor; - 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, + IHttpContextAccessor httpContextAccessor) { _plogger = plogger ?? throw new ArgumentNullException(nameof(plogger)); _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); @@ -42,7 +57,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)); + _memberUserKeyProvider = memberUserKeyProvider; + _sessionManager = sessionManager; + _requestAccessor = requestAccessor; _httpContextAccessor = httpContextAccessor; } @@ -63,12 +80,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 +141,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 +376,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 +393,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; diff --git a/src/Umbraco.Web/Macros/MemberUserKeyProvider.cs b/src/Umbraco.Web/Macros/MemberUserKeyProvider.cs new file mode 100644 index 0000000000..cb57943bad --- /dev/null +++ b/src/Umbraco.Web/Macros/MemberUserKeyProvider.cs @@ -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; + } + } +} diff --git a/src/Umbraco.Web/Models/Identity/BackOfficeIdentityUser.cs b/src/Umbraco.Web/Models/Identity/BackOfficeIdentityUser.cs index 8f77de67f2..21c965aa3d 100644 --- a/src/Umbraco.Web/Models/Identity/BackOfficeIdentityUser.cs +++ b/src/Umbraco.Web/Models/Identity/BackOfficeIdentityUser.cs @@ -5,7 +5,6 @@ using System.Collections.Specialized; using System.ComponentModel; using System.Linq; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Identity; diff --git a/src/Umbraco.Web/Models/LoginStatusModel.cs b/src/Umbraco.Web/Models/LoginStatusModel.cs index e20c7f0a0b..4b699bab38 100644 --- a/src/Umbraco.Web/Models/LoginStatusModel.cs +++ b/src/Umbraco.Web/Models/LoginStatusModel.cs @@ -1,7 +1,5 @@ using System.ComponentModel.DataAnnotations; -using Umbraco.Core; -using Umbraco.Web.Security; -using Current = Umbraco.Web.Composing.Current; + namespace Umbraco.Web.Models { @@ -16,26 +14,9 @@ namespace Umbraco.Web.Models /// public static LoginStatusModel CreateModel() { - return new LoginStatusModel(false); + return new LoginStatusModel(); } - private LoginStatusModel(bool doLookup) - { - if (doLookup && Current.UmbracoContext != null) - { - var helper = Current.Factory.GetInstance(); - var model = helper.GetCurrentLoginStatus(); - if (model != null) - { - Name = model.Name; - Username = model.Username; - Email = model.Email; - IsLoggedIn = true; - } - } - } - - /// /// The name of the member /// diff --git a/src/Umbraco.Web/Models/Mapping/CommonTreeNodeMapper.cs b/src/Umbraco.Web/Models/Mapping/CommonTreeNodeMapper.cs index d1d0857abb..aed779683f 100644 --- a/src/Umbraco.Web/Models/Mapping/CommonTreeNodeMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/CommonTreeNodeMapper.cs @@ -1,18 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web.Mvc; -using Umbraco.Core; -using Umbraco.Core.Mapping; +using System.Web.Mvc; using Umbraco.Core.Models; -using Umbraco.Core.Models.ContentEditing; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Services; -using Umbraco.Web.ContentApps; -using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using UserProfile = Umbraco.Web.Models.ContentEditing.UserProfile; namespace Umbraco.Web.Models.Mapping { diff --git a/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs index 6e16dd2d71..5fc443f692 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs @@ -1,5 +1,4 @@ -using System.Web.Mvc; -using Umbraco.Core; +using Umbraco.Core; using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Web.Models.ContentEditing; diff --git a/src/Umbraco.Web/Models/ProfileModel.cs b/src/Umbraco.Web/Models/ProfileModel.cs index 7b7b0ecb4a..0397f34849 100644 --- a/src/Umbraco.Web/Models/ProfileModel.cs +++ b/src/Umbraco.Web/Models/ProfileModel.cs @@ -3,9 +3,6 @@ using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Web.Mvc; -using Umbraco.Web.Security; -using Umbraco.Core; -using Umbraco.Core.Composing; using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Models @@ -28,7 +25,7 @@ namespace Umbraco.Web.Models MemberProperties = new List(); if (doLookup && Current.UmbracoContext != null) { - var helper = Current.Factory.GetInstance(); + var helper = Current.MembershipHelper; var model = helper.GetCurrentMemberProfileModel(); MemberProperties = model.MemberProperties; } diff --git a/src/Umbraco.Web/Models/RegisterModel.cs b/src/Umbraco.Web/Models/RegisterModel.cs index 41edea19de..1ee9307969 100644 --- a/src/Umbraco.Web/Models/RegisterModel.cs +++ b/src/Umbraco.Web/Models/RegisterModel.cs @@ -1,13 +1,8 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.Web; using System.Web.Mvc; using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Web.Security; -using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Models { @@ -20,22 +15,16 @@ namespace Umbraco.Web.Models /// public static RegisterModel CreateModel() { - return new RegisterModel(false); + return new RegisterModel(); } - private RegisterModel(bool doLookup) + private RegisterModel() { MemberTypeAlias = Constants.Conventions.MemberTypes.DefaultAlias; UsernameIsEmail = true; MemberProperties = new List(); LoginOnSuccess = true; CreatePersistentLoginCookie = true; - if (doLookup && Current.UmbracoContext != null) - { - var helper = Current.Factory.GetInstance(); - var model = helper.CreateRegistrationModel(MemberTypeAlias); - MemberProperties = model.MemberProperties; - } } diff --git a/src/Umbraco.Web/Mvc/MemberAuthorizeAttribute.cs b/src/Umbraco.Web/Mvc/MemberAuthorizeAttribute.cs index 5f81ced3f0..a99e8c1df6 100644 --- a/src/Umbraco.Web/Mvc/MemberAuthorizeAttribute.cs +++ b/src/Umbraco.Web/Mvc/MemberAuthorizeAttribute.cs @@ -48,9 +48,9 @@ namespace Umbraco.Web.Mvc } } - var helper = Current.Factory.GetInstance(); + var helper = Current.MembershipHelper; return helper.IsMemberAuthorized(AllowType.Split(','), AllowGroup.Split(','), members); - + } /// diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 0ea88d23df..da58ff1c0c 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -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.AspNet; using Umbraco.Web.Models; @@ -65,8 +65,14 @@ namespace Umbraco.Web.Runtime composition.Register(); composition.Register(); + composition.Register(); - composition.Register(); + composition.Register(Lifetime.Singleton); + composition.Register(factory => factory.GetInstance(), Lifetime.Singleton); + composition.Register(factory => factory.GetInstance(), Lifetime.Singleton); + + composition.Register(Lifetime.Singleton); + composition.Register(); composition.Register(); composition.Register(Lifetime.Singleton); @@ -76,6 +82,7 @@ namespace Umbraco.Web.Runtime composition.RegisterUnique(); // required for hybrid accessors composition.RegisterUnique(); + composition.ComposeWebMappingProfiles(); //register the install components @@ -88,6 +95,8 @@ namespace Umbraco.Web.Runtime composition.Register(factory => Roles.Enabled ? Roles.Provider : new MembersRoleProvider(factory.GetInstance())); composition.Register(Lifetime.Request); composition.Register(factory => factory.GetInstance().PublishedSnapshot.Members); + composition.RegisterUnique(); + composition.RegisterUnique(); // register accessors for cultures composition.RegisterUnique(); @@ -118,6 +127,7 @@ namespace Umbraco.Web.Runtime composition.RegisterUnique(); composition.RegisterUnique(); + composition.RegisterUnique(); composition.RegisterUnique(); diff --git a/src/Umbraco.Web/Runtime/WebRuntime.cs b/src/Umbraco.Web/Runtime/WebRuntime.cs index e898d713b8..362c99b9f1 100644 --- a/src/Umbraco.Web/Runtime/WebRuntime.cs +++ b/src/Umbraco.Web/Runtime/WebRuntime.cs @@ -9,7 +9,6 @@ using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Runtime; -using Umbraco.Web.Cache; using Umbraco.Web.Composing; using Umbraco.Web.Logging; using Current = Umbraco.Web.Composing.Current; @@ -22,7 +21,6 @@ namespace Umbraco.Web.Runtime /// On top of CoreRuntime, handles all of the web-related runtime aspects of Umbraco. public class WebRuntime : CoreRuntime { - private readonly UmbracoApplicationBase _umbracoApplication; private BuildManagerTypeFinder _typeFinder; /// @@ -30,7 +28,6 @@ namespace Umbraco.Web.Runtime /// /// public WebRuntime( - UmbracoApplicationBase umbracoApplication, Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, @@ -42,8 +39,6 @@ namespace Umbraco.Web.Runtime IMainDom mainDom): base(configs, umbracoVersion, ioHelper, logger, profiler ,new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom) { - _umbracoApplication = umbracoApplication; - Profiler = GetWebProfiler(); } @@ -102,7 +97,7 @@ namespace Umbraco.Web.Runtime protected override AppCaches GetAppCaches() => new AppCaches( // we need to have the dep clone runtime cache provider to ensure // all entities are cached properly (cloned in and cloned out) - new DeepCloneAppCache(new WebCachingAppCache(HttpRuntime.Cache, TypeFinder)), + new DeepCloneAppCache(new ObjectCacheAppCache(TypeFinder)), // we need request based cache when running in web-based context new HttpRequestAppCache(() => HttpContext.Current?.Items, TypeFinder), new IsolatedCaches(type => diff --git a/src/Umbraco.Web/Security/EmailService.cs b/src/Umbraco.Web/Security/EmailService.cs index e6454544ab..aabd5a1ead 100644 --- a/src/Umbraco.Web/Security/EmailService.cs +++ b/src/Umbraco.Web/Security/EmailService.cs @@ -1,9 +1,6 @@ -using System; -using System.ComponentModel; -using System.Net.Mail; +using System.Net.Mail; using System.Threading.Tasks; using Microsoft.AspNet.Identity; -using Umbraco.Core.Configuration; namespace Umbraco.Core.Security { diff --git a/src/Umbraco.Web/Security/PublicAccessChecker.cs b/src/Umbraco.Web/Security/PublicAccessChecker.cs new file mode 100644 index 0000000000..1ea22586e5 --- /dev/null +++ b/src/Umbraco.Web/Security/PublicAccessChecker.cs @@ -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; + private readonly IPublicAccessService _publicAccessService; + private readonly IContentService _contentService; + + public PublicAccessChecker(Lazy 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(Constants.Conventions.Member.IsApproved)) + { + return PublicAccessStatus.LockedOut; + } + + return PublicAccessStatus.AccessAccepted; + } + } +} diff --git a/src/Umbraco.Web/Security/SessionIdValidator.cs b/src/Umbraco.Web/Security/SessionIdValidator.cs index 8f6782aac2..11fb28c197 100644 --- a/src/Umbraco.Web/Security/SessionIdValidator.cs +++ b/src/Umbraco.Web/Security/SessionIdValidator.cs @@ -10,7 +10,7 @@ using Microsoft.Owin.Security.Cookies; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.IO; -using Umbraco.Core.Security; + using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Security diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index ac08258ee2..8cf310d7ee 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -2,16 +2,15 @@ using System; using System.Globalization; using System.IO; using System.Linq; -using System.Web; using System.Web.Mvc; using System.Web.Routing; +using Umbraco.Core; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Web.Routing; -using Umbraco.Core.Strings; namespace Umbraco.Web.Templates { @@ -46,10 +45,11 @@ namespace Umbraco.Web.Templates { if (writer == null) throw new ArgumentNullException(nameof(writer)); + var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); // instantiate a request and process // important to use CleanedUmbracoUrl - lowercase path-only version of the current url, though this isn't going to matter // terribly much for this implementation since we are just creating a doc content request to modify it's properties manually. - var contentRequest = _publishedRouter.CreateRequest(_umbracoContextAccessor.UmbracoContext); + var contentRequest = _publishedRouter.CreateRequest(umbracoContext); var doc = contentRequest.UmbracoContext.Content.GetById(pageId); @@ -62,7 +62,7 @@ namespace Umbraco.Web.Templates //in some cases the UmbracoContext will not have a PublishedRequest assigned to it if we are not in the //execution of a front-end rendered page. In this case set the culture to the default. //set the culture to the same as is currently rendering - if (_umbracoContextAccessor.UmbracoContext.PublishedRequest == null) + if (umbracoContext.PublishedRequest == null) { var defaultLanguage = _languageService.GetAllLanguages().FirstOrDefault(); contentRequest.Culture = defaultLanguage == null @@ -71,7 +71,7 @@ namespace Umbraco.Web.Templates } else { - contentRequest.Culture = _umbracoContextAccessor.UmbracoContext.PublishedRequest.Culture; + contentRequest.Culture = umbracoContext.PublishedRequest.Culture; } //set the doc that was found by id diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index fb96847eb4..fad79b0296 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -134,10 +134,11 @@ + + - @@ -166,6 +167,8 @@ + + @@ -185,12 +188,9 @@ - - - @@ -200,6 +200,7 @@ + @@ -214,7 +215,6 @@ - @@ -273,7 +273,6 @@ - @@ -305,7 +304,6 @@ - @@ -322,7 +320,6 @@ - @@ -343,7 +340,6 @@ - @@ -526,9 +522,6 @@ - - - @@ -553,7 +546,6 @@ - Code diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs index 2375e4a57b..97f06d3c0f 100644 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -30,7 +30,7 @@ namespace Umbraco.Web var mainDom = new MainDom(logger, hostingEnvironment, mainDomLock); - return new WebRuntime(this, configs, umbracoVersion, ioHelper, logger, profiler, hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom); + return new WebRuntime(configs, umbracoVersion, ioHelper, logger, profiler, hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom); } } } diff --git a/src/Umbraco.Web/UmbracoApplicationBase.cs b/src/Umbraco.Web/UmbracoApplicationBase.cs index 12925604db..1111d028f8 100644 --- a/src/Umbraco.Web/UmbracoApplicationBase.cs +++ b/src/Umbraco.Web/UmbracoApplicationBase.cs @@ -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(), coreDebug, ioHelper, new FrameworkMarchal()); + var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, new AspNetSessionManager(), () => _factory?.GetInstance(), 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); diff --git a/src/Umbraco.Web/WebApi/MemberAuthorizeAttribute.cs b/src/Umbraco.Web/WebApi/MemberAuthorizeAttribute.cs index bc1e9a4318..6d4607cfba 100644 --- a/src/Umbraco.Web/WebApi/MemberAuthorizeAttribute.cs +++ b/src/Umbraco.Web/WebApi/MemberAuthorizeAttribute.cs @@ -46,7 +46,7 @@ namespace Umbraco.Web.WebApi } } - var helper = Current.Factory.GetInstance(); + var helper = Current.MembershipHelper; return helper.IsMemberAuthorized(AllowType.Split(','), AllowGroup.Split(','), members); }