Example #1
0
    private async Task SetUmbracoRouteValues(ActionExecutingContext context, IPublishedContent?content)
    {
        if (content != null)
        {
            UriUtility uriUtility = context.HttpContext.RequestServices.GetRequiredService <UriUtility>();

            var originalRequestUrl = new Uri(context.HttpContext.Request.GetEncodedUrl());
            Uri cleanedUrl         = uriUtility.UriToUmbraco(originalRequestUrl);

            IPublishedRouter router = context.HttpContext.RequestServices.GetRequiredService <IPublishedRouter>();

            IPublishedRequestBuilder requestBuilder = await router.CreateRequestAsync(cleanedUrl);

            requestBuilder.SetPublishedContent(content);
            IPublishedRequest publishedRequest = requestBuilder.Build();

            var routeValues = new UmbracoRouteValues(
                publishedRequest,
                (ControllerActionDescriptor)context.ActionDescriptor);

            context.HttpContext.Features.Set(routeValues);
        }
        else
        {
            // if there is no content then it should be a not found
            context.Result = new NotFoundResult();
        }
    }
        private async Task SetUmbracoRouteValues(ActionExecutingContext context, IPublishedContent content)
        {
            if (content != null)
            {
                IUmbracoContextAccessor umbracoContextAccessor = context.HttpContext.RequestServices.GetRequiredService <IUmbracoContextAccessor>();
                IPublishedRouter        router = context.HttpContext.RequestServices.GetRequiredService <IPublishedRouter>();

                var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext();

                IPublishedRequestBuilder requestBuilder = await router.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl);

                requestBuilder.SetPublishedContent(content);
                IPublishedRequest publishedRequest = requestBuilder.Build();

                var routeValues = new UmbracoRouteValues(
                    publishedRequest,
                    (ControllerActionDescriptor)context.ActionDescriptor);

                context.HttpContext.Features.Set(routeValues);
            }
            else
            {
                // if there is no content then it should be a not found
                context.Result = new NotFoundResult();
            }
        }
Example #3
0
    /// <inheritdoc />
    public async Task <UmbracoRouteValues> CreateAsync(HttpContext httpContext, IPublishedRequest request)
    {
        if (httpContext is null)
        {
            throw new ArgumentNullException(nameof(httpContext));
        }

        if (request is null)
        {
            throw new ArgumentNullException(nameof(request));
        }

        string?customActionName = GetTemplateName(request);

        // The default values for the default controller/action
        var def = new UmbracoRouteValues(
            request,
            _defaultControllerDescriptor.Value,
            customActionName);

        def = CheckHijackedRoute(httpContext, def, out var hasHijackedRoute);

        def = await CheckNoTemplateAsync(httpContext, def, hasHijackedRoute);

        return(def);
    }
Example #4
0
    /// <summary>
    ///     Check if the route is hijacked and return new route values
    /// </summary>
    private UmbracoRouteValues CheckHijackedRoute(
        HttpContext httpContext, UmbracoRouteValues def, out bool hasHijackedRoute)
    {
        IPublishedRequest request = def.PublishedRequest;

        var customControllerName = request.PublishedContent?.ContentType?.Alias;

        if (customControllerName != null)
        {
            ControllerActionDescriptor?descriptor =
                _controllerActionSearcher.Find <IRenderController>(httpContext, customControllerName, def.TemplateName);
            if (descriptor != null)
            {
                hasHijackedRoute = true;

                return(new UmbracoRouteValues(
                           request,
                           descriptor,
                           def.TemplateName));
            }
        }

        hasHijackedRoute = false;
        return(def);
    }
Example #5
0
        public async Task Update_Request_To_Not_Found_When_No_Template()
        {
            UmbracoRouteValuesFactory factory = GetFactory(out Mock <IPublishedRouter> publishedRouter, out _, out IPublishedRequest request);

            UmbracoRouteValues result = await factory.CreateAsync(new DefaultHttpContext(), request);

            // The request has content, no template, no hijacked route and no disabled template features so UpdateRequestToNotFound will be called
            publishedRouter.Verify(m => m.UpdateRequestAsync(It.IsAny <IPublishedRequest>(), null), Times.Once);
        }
Example #6
0
        public async Task Adds_Result_To_Route_Value_Dictionary()
        {
            UmbracoRouteValuesFactory factory = GetFactory(out _, out IOptions <UmbracoRenderingDefaultsOptions> renderingDefaults, out IPublishedRequest request);

            UmbracoRouteValues result = await factory.CreateAsync(new DefaultHttpContext(), request);

            Assert.IsNotNull(result);
            Assert.AreEqual(renderingDefaults.Value.DefaultControllerType, result.ControllerType);
            Assert.AreEqual(UmbracoRouteValues.DefaultActionName, result.ActionName);
            Assert.IsNull(result.TemplateName);
        }
Example #7
0
        /// <summary>
        /// Gets the <see cref="UmbracoRouteValues"/>
        /// </summary>
        protected UmbracoRouteValues GetUmbracoRouteValues(ResultExecutingContext context)
        {
            UmbracoRouteValues routeVals = context.HttpContext.Features.Get <UmbracoRouteValues>();

            if (routeVals == null)
            {
                throw new InvalidOperationException($"No {nameof(UmbracoRouteValues)} feature was found in the HttpContext");
            }

            return(routeVals);
        }
        public async Task Returns_Null_RouteValueDictionary_When_No_Content()
        {
            IUmbracoContext    umbracoContext = GetUmbracoContext(true);
            IPublishedRequest  request        = Mock.Of <IPublishedRequest>(x => x.PublishedContent == null);
            UmbracoRouteValues routeValues    = GetRouteValues(request);

            UmbracoRouteValueTransformer transformer = GetTransformerWithRunState(
                Mock.Of <IUmbracoContextAccessor>(x => x.TryGetUmbracoContext(out umbracoContext)),
                router: GetRouter(request),
                routeValuesFactory: GetRouteValuesFactory(request));

            RouteValueDictionary result = await transformer.TransformAsync(new DefaultHttpContext(), new RouteValueDictionary());

            Assert.IsNull(result);
        }
        public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
        {
            if (AllInvalid(candidates))
            {
                UmbracoRouteValues umbracoRouteValues = httpContext.Features.Get <UmbracoRouteValues>();
                if (umbracoRouteValues?.PublishedRequest != null &&
                    !umbracoRouteValues.PublishedRequest.HasPublishedContent() &&
                    umbracoRouteValues.PublishedRequest.ResponseStatusCode == StatusCodes.Status404NotFound)
                {
                    // not found/404
                    httpContext.SetEndpoint(_notFound.Value);
                }
            }

            return(Task.CompletedTask);
        }
        public async Task Assigns_Values_To_RouteValueDictionary_When_Content()
        {
            IUmbracoContext    umbracoContext = GetUmbracoContext(true);
            IPublishedRequest  request        = Mock.Of <IPublishedRequest>(x => x.PublishedContent == Mock.Of <IPublishedContent>());
            UmbracoRouteValues routeValues    = GetRouteValues(request);

            UmbracoRouteValueTransformer transformer = GetTransformerWithRunState(
                Mock.Of <IUmbracoContextAccessor>(x => x.TryGetUmbracoContext(out umbracoContext)),
                router: GetRouter(request),
                routeValuesFactory: GetRouteValuesFactory(request));

            RouteValueDictionary result = await transformer.TransformAsync(new DefaultHttpContext(), new RouteValueDictionary());

            Assert.AreEqual(routeValues.ControllerName, result[ControllerToken]);
            Assert.AreEqual(routeValues.ActionName, result[ActionToken]);
        }
        public async Task Assigns_UmbracoRouteValues_To_HttpContext_Feature()
        {
            IUmbracoContext   umbracoContext = GetUmbracoContext(true);
            IPublishedRequest request        = Mock.Of <IPublishedRequest>(x => x.PublishedContent == Mock.Of <IPublishedContent>());

            UmbracoRouteValueTransformer transformer = GetTransformerWithRunState(
                Mock.Of <IUmbracoContextAccessor>(x => x.TryGetUmbracoContext(out umbracoContext)),
                router: GetRouter(request),
                routeValuesFactory: GetRouteValuesFactory(request));

            var httpContext             = new DefaultHttpContext();
            RouteValueDictionary result = await transformer.TransformAsync(httpContext, new RouteValueDictionary());

            UmbracoRouteValues routeVals = httpContext.Features.Get <UmbracoRouteValues>();

            Assert.IsNotNull(routeVals);
            Assert.AreEqual(routeVals.PublishedRequest, umbracoContext.PublishedRequest);
        }
Example #12
0
    /// <summary>
    ///     Special check for when no template or hijacked route is done which needs to re-run through the routing pipeline
    ///     again for last chance finders
    /// </summary>
    private async Task <UmbracoRouteValues> CheckNoTemplateAsync(
        HttpContext httpContext, UmbracoRouteValues def, bool hasHijackedRoute)
    {
        IPublishedRequest request = def.PublishedRequest;

        // Here we need to check if there is no hijacked route and no template assigned but there is a content item.
        // If this is the case we want to return a blank page.
        // We also check if templates have been disabled since if they are then we're allowed to render even though there's no template,
        // for example for json rendering in headless.
        if (request.HasPublishedContent() &&
            !request.HasTemplate() &&
            !_umbracoFeatures.Disabled.DisableTemplates &&
            !hasHijackedRoute)
        {
            IPublishedContent?content = request.PublishedContent;

            // This is basically a 404 even if there is content found.
            // We then need to re-run this through the pipeline for the last
            // chance finders to work.
            // Set to null since we are telling it there is no content.
            request = await _publishedRouter.UpdateRequestAsync(request, null);

            if (request == null)
            {
                throw new InvalidOperationException(
                          $"The call to {nameof(IPublishedRouter.UpdateRequestAsync)} cannot return null");
            }

            string?customActionName = GetTemplateName(request);

            def = new UmbracoRouteValues(
                request,
                def.ControllerActionDescriptor,
                customActionName);

            // if the content has changed, we must then again check for hijacked routes
            if (content != request.PublishedContent)
            {
                def = CheckHijackedRoute(httpContext, def, out _);
            }
        }

        return(def);
    }
        public async Task Null_When_No_Content_On_PublishedRequest()
        {
            IUmbracoContext   umbracoContext = GetUmbracoContext(true);
            IPublishedRequest request        = Mock.Of <IPublishedRequest>(x => x.PublishedContent == null);

            UmbracoRouteValueTransformer transformer = GetTransformerWithRunState(
                Mock.Of <IUmbracoContextAccessor>(x => x.TryGetUmbracoContext(out umbracoContext)),
                router: GetRouter(request),
                routeValuesFactory: GetRouteValuesFactory(request));

            var httpContext             = new DefaultHttpContext();
            RouteValueDictionary result = await transformer.TransformAsync(httpContext, new RouteValueDictionary());

            Assert.IsNull(result);

            UmbracoRouteValues routeVals = httpContext.Features.Get <UmbracoRouteValues>();

            Assert.AreEqual(routeVals.PublishedRequest.GetRouteResult(), UmbracoRouteResult.NotFound);
        }
Example #14
0
        /// <summary>
        /// Deals with custom headers for the umbraco request
        /// </summary>
        public override void OnResultExecuting(ResultExecutingContext context)
        {
            UmbracoRouteValues routeVals = GetUmbracoRouteValues(context);
            IPublishedRequest  pcr       = routeVals.PublishedRequest;

            // now we can deal with headers, etc...
            if (pcr.ResponseStatusCode.HasValue)
            {
                // set status code -- even for redirects
                context.HttpContext.Response.StatusCode = pcr.ResponseStatusCode.Value;
            }

            AddCacheControlHeaders(context, pcr);

            if (pcr.Headers != null)
            {
                foreach (KeyValuePair <string, string> header in pcr.Headers)
                {
                    context.HttpContext.Response.Headers.Append(header.Key, header.Value);
                }
            }
        }
Example #15
0
        public void Mock_Current_Page()
        {
            var globalSettings = new GlobalSettings();
            IHostingEnvironment         hostingEnvironment         = Mock.Of <IHostingEnvironment>();
            IBackOfficeSecurityAccessor backofficeSecurityAccessor = Mock.Of <IBackOfficeSecurityAccessor>();

            Mock.Get(backofficeSecurityAccessor).Setup(x => x.BackOfficeSecurity).Returns(Mock.Of <IBackOfficeSecurity>());
            var umbracoContextFactory = TestUmbracoContextFactory.Create(globalSettings, _umbracoContextAccessor);

            UmbracoContextReference umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext();
            IUmbracoContext         umbracoContext          = umbracoContextReference.UmbracoContext;

            var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext);

            IPublishedContent content = Mock.Of <IPublishedContent>(publishedContent => publishedContent.Id == 12345);
            var builder = new PublishedRequestBuilder(umbracoContext.CleanedUmbracoUrl, Mock.Of <IFileService>());

            builder.SetPublishedContent(content);
            IPublishedRequest publishedRequest = builder.Build();

            var routeDefinition = new UmbracoRouteValues(publishedRequest, null);

            var httpContext = new DefaultHttpContext();

            httpContext.Features.Set(routeDefinition);

            var ctrl = new TestSurfaceController(umbracoContextAccessor, Mock.Of <IPublishedContentQuery>(), Mock.Of <IPublishedUrlProvider>())
            {
                ControllerContext = new ControllerContext()
                {
                    HttpContext = httpContext,
                    RouteData   = new RouteData()
                }
            };

            var result = ctrl.GetContentFromCurrentPage() as PublishedContentResult;

            Assert.AreEqual(12345, result.Content.Id);
        }
    private async Task <UmbracoRouteValues> SetPublishedContentAsOtherPageAsync(
        HttpContext httpContext, IPublishedRequest?publishedRequest, int pageId)
    {
        if (pageId != publishedRequest?.PublishedContent?.Id)
        {
            IUmbracoContext   umbracoContext   = _umbracoContextAccessor.GetRequiredUmbracoContext();
            IPublishedContent?publishedContent = umbracoContext.PublishedSnapshot.Content?.GetById(pageId);
            if (publishedContent is null || publishedRequest is null)
            {
                throw new InvalidOperationException("No content found by id " + pageId);
            }

            IPublishedRequest reRouted = await _publishedRouter.UpdateRequestAsync(publishedRequest, publishedContent);

            // we need to change the content item that is getting rendered so we have to re-create UmbracoRouteValues.
            UmbracoRouteValues updatedRouteValues = await _umbracoRouteValuesFactory.CreateAsync(httpContext, reRouted);

            return(updatedRouteValues);
        }

        throw new InvalidOperationException(
                  "Public Access rule has a redirect node set to itself, nothing can be routed.");
    }
Example #17
0
    /// <inheritdoc />
    public async Task <UmbracoRouteValues> CreateAsync(HttpContext httpContext, IPublishedRequest request)
    {
        if (httpContext is null)
        {
            throw new ArgumentNullException(nameof(httpContext));
        }

        if (request is null)
        {
            throw new ArgumentNullException(nameof(request));
        }

        string?customActionName = null;

        // check that a template is defined), if it doesn't and there is a hijacked route it will just route
        // to the index Action
        if (request.HasTemplate())
        {
            // the template Alias should always be already saved with a safe name.
            // if there are hyphens in the name and there is a hijacked route, then the Action will need to be attributed
            // with the action name attribute.
            customActionName = request.GetTemplateAlias()?.Split('.')[0].ToSafeAlias(_shortStringHelper);
        }

        // The default values for the default controller/action
        var def = new UmbracoRouteValues(
            request,
            _defaultControllerDescriptor.Value,
            customActionName);

        def = CheckHijackedRoute(httpContext, def, out var hasHijackedRoute);

        def = await CheckNoTemplateAsync(httpContext, def, hasHijackedRoute);

        return(def);
    }
        /// <inheritdoc/>
        public override async ValueTask <RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
        {
            // If we aren't running, then we have nothing to route
            if (_runtime.Level != RuntimeLevel.Run)
            {
                return(null);
            }
            // will be null for any client side requests like JS, etc...
            if (!_umbracoContextAccessor.TryGetUmbracoContext(out IUmbracoContext umbracoContext))
            {
                return(null);
            }

            if (!_routableDocumentFilter.IsDocumentRequest(httpContext.Request.Path))
            {
                return(null);
            }

            // Don't execute if there are already UmbracoRouteValues assigned.
            // This can occur if someone else is dynamically routing and in which case we don't want to overwrite
            // the routing work being done there.
            UmbracoRouteValues umbracoRouteValues = httpContext.Features.Get <UmbracoRouteValues>();

            if (umbracoRouteValues != null)
            {
                return(null);
            }

            // Check if there is no existing content and return the no content controller
            if (!umbracoContext.Content.HasContent())
            {
                return(new RouteValueDictionary
                {
                    [ControllerToken] = ControllerExtensions.GetControllerName <RenderNoContentController>(),
                    [ActionToken] = nameof(RenderNoContentController.Index)
                });
            }

            IPublishedRequest publishedRequest = await RouteRequestAsync(umbracoContext);

            umbracoRouteValues = await _routeValuesFactory.CreateAsync(httpContext, publishedRequest);

            // now we need to do some public access checks
            umbracoRouteValues = await _publicAccessRequestHandler.RewriteForPublishedContentAccessAsync(httpContext, umbracoRouteValues);

            // Store the route values as a httpcontext feature
            httpContext.Features.Set(umbracoRouteValues);

            // Need to check if there is form data being posted back to an Umbraco URL
            PostedDataProxyInfo postedInfo = GetFormInfo(httpContext, values);

            if (postedInfo != null)
            {
                return(HandlePostedValues(postedInfo, httpContext));
            }

            UmbracoRouteResult?routeResult = umbracoRouteValues?.PublishedRequest?.GetRouteResult();

            if (!routeResult.HasValue || routeResult == UmbracoRouteResult.NotFound)
            {
                // No content was found, not by any registered 404 handlers and
                // not by the IContentLastChanceFinder. In this case we want to return
                // our default 404 page but we cannot return route values now because
                // it's possible that a developer is handling dynamic routes too.
                // Our 404 page will be handled with the NotFoundSelectorPolicy
                return(null);
            }

            // See https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.routing.dynamicroutevaluetransformer.transformasync?view=aspnetcore-5.0#Microsoft_AspNetCore_Mvc_Routing_DynamicRouteValueTransformer_TransformAsync_Microsoft_AspNetCore_Http_HttpContext_Microsoft_AspNetCore_Routing_RouteValueDictionary_
            // We should apparenlty not be modified these values.
            // So we create new ones.
            var newValues = new RouteValueDictionary
            {
                [ControllerToken] = umbracoRouteValues.ControllerName
            };

            if (string.IsNullOrWhiteSpace(umbracoRouteValues.ActionName) == false)
            {
                newValues[ActionToken] = umbracoRouteValues.ActionName;
            }

            return(newValues);
        }
    /// <inheritdoc />
    public async Task <UmbracoRouteValues?> RewriteForPublishedContentAccessAsync(
        HttpContext httpContext,
        UmbracoRouteValues routeValues)
    {
        // because these might loop, we have to have some sort of infinite loop detection
        var                i       = 0;
        const int          maxLoop = 8;
        PublicAccessStatus publicAccessStatus;

        do
        {
            _logger.LogDebug(nameof(RewriteForPublishedContentAccessAsync) + ": Loop {LoopCounter}", i);

            IPublishedContent?publishedContent = routeValues.PublishedRequest?.PublishedContent;
            if (publishedContent == null)
            {
                return(routeValues);
            }

            var path = publishedContent.Path;

            Attempt <PublicAccessEntry?> publicAccessAttempt = _publicAccessService.IsProtected(path);

            if (publicAccessAttempt.Success)
            {
                _logger.LogDebug("EnsurePublishedContentAccess: Page is protected, check for access");

                // manually authenticate the request
                AuthenticateResult authResult =
                    await httpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme);

                if (authResult.Succeeded)
                {
                    // set the user to the auth result. we need to do this here because this occurs
                    // before the authentication middleware.
                    // NOTE: It would be possible to just pass the authResult to the HasMemberAccessToContentAsync method
                    // instead of relying directly on the user assigned to the http context, and then the auth middleware
                    // will run anyways and assign the user. Perhaps that is a little cleaner, but would require more code
                    // changes right now, and really it's not any different in the end result.
                    httpContext.SetPrincipalForRequest(authResult.Principal);
                }

                publicAccessStatus = await _publicAccessChecker.HasMemberAccessToContentAsync(publishedContent.Id);

                switch (publicAccessStatus)
                {
                case PublicAccessStatus.NotLoggedIn:
                    // redirect if this is not the login page
                    if (publicAccessAttempt.Result !.LoginNodeId != publishedContent.Id)
                    {
                        _logger.LogDebug("EnsurePublishedContentAccess: Not logged in, redirect to login page");
                        routeValues = await SetPublishedContentAsOtherPageAsync(
                            httpContext, routeValues.PublishedRequest, publicAccessAttempt.Result !.LoginNodeId);
                    }

                    break;

                case PublicAccessStatus.AccessDenied:
                    // Redirect if this is not the access denied page
                    if (publicAccessAttempt.Result !.NoAccessNodeId != publishedContent.Id)
                    {
                        _logger.LogDebug(
                            "EnsurePublishedContentAccess: Current member has not access, redirect to error page");
                        routeValues = await SetPublishedContentAsOtherPageAsync(
                            httpContext, routeValues.PublishedRequest, publicAccessAttempt.Result !.NoAccessNodeId);
                    }

                    break;

                case PublicAccessStatus.LockedOut:
                    _logger.LogDebug("Current member is locked out, redirect to error page");
                    routeValues = await SetPublishedContentAsOtherPageAsync(
                        httpContext, routeValues.PublishedRequest, publicAccessAttempt.Result !.NoAccessNodeId);

                    break;

                case PublicAccessStatus.NotApproved:
                    _logger.LogDebug("Current member is unapproved, redirect to error page");
                    routeValues = await SetPublishedContentAsOtherPageAsync(
                        httpContext, routeValues.PublishedRequest, publicAccessAttempt.Result !.NoAccessNodeId);

                    break;

                case PublicAccessStatus.AccessAccepted:
                    _logger.LogDebug("Current member has access");
                    break;
                }
            }
            else
            {
                publicAccessStatus = PublicAccessStatus.AccessAccepted;
                _logger.LogDebug("EnsurePublishedContentAccess: Page is not protected");
            }

            // loop until we have access or reached max loops
        } while (publicAccessStatus != PublicAccessStatus.AccessAccepted && i++ < maxLoop);

        if (i == maxLoop)
        {
            _logger.LogDebug(nameof(RewriteForPublishedContentAccessAsync) +
                             ": Looks like we are running into an infinite loop, abort");
        }

        return(routeValues);
    }