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(); } }
/// <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); }
/// <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); }
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); }
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); }
/// <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); }
/// <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); }
/// <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); } } }
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."); }
/// <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); }