diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/LocalizationSetContentPickerAdminController.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/LocalizationSetContentPickerAdminController.cs index f0a7ca5d114..3818679bd7e 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/LocalizationSetContentPickerAdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/LocalizationSetContentPickerAdminController.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using OrchardCore.Admin; using OrchardCore.ContentFields.Settings; @@ -9,9 +10,11 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata; using OrchardCore.ContentManagement.Records; +using OrchardCore.Contents; using OrchardCore.Modules; using YesSql; using YesSql.Services; +using IHttpContextAccessor = Microsoft.AspNetCore.Http.IHttpContextAccessor; namespace OrchardCore.ContentFields.Controllers { @@ -23,18 +26,23 @@ public class LocalizationSetContentPickerAdminController : Controller private readonly IContentLocalizationManager _contentLocalizationManager; private readonly IContentManager _contentManager; private readonly ISession _session; + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; public LocalizationSetContentPickerAdminController( IContentDefinitionManager contentDefinitionManager, IContentLocalizationManager contentLocalizationManager, IContentManager contentManager, - ISession session - ) + ISession session, + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor) { _contentDefinitionManager = contentDefinitionManager; _contentLocalizationManager = contentLocalizationManager; _contentManager = contentManager; _session = session; + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; } [HttpGet] @@ -71,12 +79,15 @@ public async Task SearchLocalizationSets(string part, string fiel foreach (var contentItem in cleanedContentItems) { - results.Add(new VueMultiselectItemViewModel + if (await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.ViewContent, contentItem)) { - Id = contentItem.Key, //localization set - DisplayText = contentItem.Value.ToString(), - HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem.Value) - }); + results.Add(new VueMultiselectItemViewModel + { + Id = contentItem.Key, //localization set + DisplayText = contentItem.Value.ToString(), + HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem.Value) + }); + } } return new ObjectResult(results); diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentController.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentController.cs index 047258b8006..e82dfd0779e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentController.cs @@ -1,10 +1,13 @@ using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using OrchardCore.Admin; using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Display; +using OrchardCore.Contents; using OrchardCore.DisplayManagement.ModelBinding; using YesSql; +using IHttpContextAccessor = Microsoft.AspNetCore.Http.IHttpContextAccessor; namespace OrchardCore.Demo.Controllers { @@ -14,17 +17,23 @@ public class ContentController : Controller private readonly IContentManager _contentManager; private readonly ISession _session; private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; public ContentController( IContentManager contentManager, IContentItemDisplayManager contentDisplay, ISession session, - IUpdateModelAccessor updateModelAccessor) + IUpdateModelAccessor updateModelAccessor, + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor) { _contentManager = contentManager; _contentDisplay = contentDisplay; _session = session; _updateModelAccessor = updateModelAccessor; + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; } public async Task Display(string contentItemId) @@ -36,6 +45,11 @@ public async Task Display(string contentItemId) return NotFound(); } + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.ViewContent, contentItem)) + { + return Forbid(); + } + var shape = await _contentDisplay.BuildDisplayAsync(contentItem, _updateModelAccessor.ModelUpdater); return View(shape); } @@ -50,6 +64,11 @@ public async Task Edit(string contentItemId) return NotFound(); } + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.EditContent, contentItem)) + { + return Forbid(); + } + var shape = await _contentDisplay.BuildEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); return View(shape); } @@ -64,6 +83,11 @@ public async Task EditPost(string contentItemId) return NotFound(); } + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.EditContent, contentItem)) + { + return Forbid(); + } + var shape = await _contentDisplay.UpdateEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); if (!ModelState.IsValid) diff --git a/src/OrchardCore.Modules/OrchardCore.Features/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Features/Controllers/AdminController.cs index 6b811e56da9..96e1244588d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Features/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Features/Controllers/AdminController.cs @@ -112,6 +112,11 @@ public async Task Features(BulkActionViewModel model, bool? force) [HttpPost] public async Task Disable(string id) { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageFeatures)) + { + return Forbid(); + } + var feature = (await _shellFeaturesManager.GetAvailableFeaturesAsync()) .FirstOrDefault(f => !f.IsTheme() && f.Id == id); @@ -134,6 +139,11 @@ public async Task Disable(string id) [HttpPost] public async Task Enable(string id) { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageFeatures)) + { + return Forbid(); + } + var feature = (await _shellFeaturesManager.GetAvailableFeaturesAsync()) .FirstOrDefault(f => !f.IsTheme() && f.Id == id); diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs index aae4ee9e9bd..ad15b148822 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs @@ -85,6 +85,11 @@ public async Task Create() [HttpPost] public async Task Create(CreateRoleViewModel model) { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRoles)) + { + return Forbid(); + } + if (ModelState.IsValid) { model.RoleName = model.RoleName.Trim(); diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoContentDriver.cs b/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoContentDriver.cs index 940ee1c869f..7197d857d47 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoContentDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoContentDriver.cs @@ -1,4 +1,6 @@ using System; +using System.Text.Encodings.Web; +using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.Html; using OrchardCore.ContentManagement; @@ -21,6 +23,7 @@ public class SeoContentDriver : ContentDisplayDriver private readonly IPageTitleBuilder _pageTitleBuilder; private readonly IResourceManager _resourceManager; private readonly IShortcodeService _shortcodeService; + private readonly HtmlEncoder _htmlEncoder; private bool _primaryContentRendered { get; set; } @@ -28,13 +31,15 @@ public class SeoContentDriver : ContentDisplayDriver IContentManager contentManager, IPageTitleBuilder pageTitleBuilder, IResourceManager resourceManager, - IShortcodeService shortcodeService + IShortcodeService shortcodeService, + HtmlEncoder htmlEncoder ) { _contentManager = contentManager; _pageTitleBuilder = pageTitleBuilder; _resourceManager = resourceManager; _shortcodeService = shortcodeService; + _htmlEncoder = htmlEncoder; } public override async Task DisplayAsync(ContentItem contentItem, BuildDisplayContext context) @@ -60,9 +65,14 @@ public override async Task DisplayAsync(ContentItem contentItem, return null; } + var shortCodeContext = new Context + { + ["ContentItem"] = contentItem + }; + if (!String.IsNullOrEmpty(aspect.PageTitle)) { - _pageTitleBuilder.SetFixedTitle(new HtmlString(await RenderAsync(aspect.PageTitle, contentItem))); + _pageTitleBuilder.SetFixedTitle(new HtmlString(_htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.PageTitle, shortCodeContext)))); } if (!String.IsNullOrEmpty(aspect.MetaDescription)) @@ -70,7 +80,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Name = "description", - Content = await RenderAsync(aspect.MetaDescription, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaDescription, shortCodeContext)) }); } @@ -79,7 +89,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Name = "keywords", - Content = await RenderAsync(aspect.MetaKeywords, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaKeywords, shortCodeContext)) }); } @@ -97,7 +107,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Name = "robots", - Content = await RenderAsync(aspect.MetaRobots, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaRobots, shortCodeContext)) }); } @@ -105,11 +115,11 @@ public override async Task DisplayAsync(ContentItem contentItem, { // Generate a new meta entry as the builder is preopulated. _resourceManager.RegisterMeta(new MetaEntry( - await RenderAsync(customMetaTag.Name, contentItem), - await RenderAsync(customMetaTag.Property, contentItem), - await RenderAsync(customMetaTag.Content, contentItem), - await RenderAsync(customMetaTag.HttpEquiv, contentItem), - await RenderAsync(customMetaTag.Charset, contentItem))); + _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Name, shortCodeContext)), + _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Property, shortCodeContext)), + _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Content, shortCodeContext)), + _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.HttpEquiv, shortCodeContext)), + _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Charset, shortCodeContext)))); } // OpenGraph. @@ -118,7 +128,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Property = "og:type", - Content = await RenderAsync(aspect.OpenGraphType, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphType, shortCodeContext)) }); } @@ -127,7 +137,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Property = "og:title", - Content = await RenderAsync(aspect.OpenGraphTitle, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphTitle, shortCodeContext)) }); } @@ -136,7 +146,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Property = "og:description", - Content = await RenderAsync(aspect.OpenGraphDescription, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphDescription, shortCodeContext)) }); } @@ -145,7 +155,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Property = "og:image", - Content = await RenderAsync(aspect.OpenGraphImage, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphImage, shortCodeContext)) }); } @@ -154,7 +164,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Property = "og:image:alt", - Content = await RenderAsync(aspect.OpenGraphImageAlt, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphImageAlt, shortCodeContext)) }); } @@ -163,7 +173,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Property = "og:url", - Content = await RenderAsync(aspect.OpenGraphUrl, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphUrl, shortCodeContext)) }); } @@ -172,7 +182,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Property = "og:site_name", - Content = await RenderAsync(aspect.OpenGraphSiteName, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphSiteName, shortCodeContext)) }); } @@ -181,7 +191,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Property = "fb:app_id", - Content = await RenderAsync(aspect.OpenGraphAppId, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphAppId, shortCodeContext)) }); } @@ -190,7 +200,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Property = "og:locale", - Content = await RenderAsync(aspect.OpenGraphLocale, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphLocale, shortCodeContext)) }); } @@ -200,7 +210,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Property = "twitter:card", - Content = await RenderAsync(aspect.TwitterCard, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterCard, shortCodeContext)) }); } @@ -209,7 +219,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Property = "twitter:site", - Content = await RenderAsync(aspect.TwitterSite, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterSite, shortCodeContext)) }); } @@ -218,7 +228,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Name = "twitter:title", - Content = await RenderAsync(aspect.TwitterTitle, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterTitle, shortCodeContext)) }); } @@ -227,7 +237,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Name = "twitter:description", - Content = await RenderAsync(aspect.TwitterDescription, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterDescription, shortCodeContext)) }); } @@ -236,7 +246,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Name = "twitter:image", - Content = await RenderAsync(aspect.TwitterImage, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterImage, shortCodeContext)) }); } @@ -245,7 +255,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Name = "twitter:image:alt", - Content = await RenderAsync(aspect.TwitterImageAlt, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterImageAlt, shortCodeContext)) }); } @@ -254,7 +264,7 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Name = "twitter:creator", - Content = await RenderAsync(aspect.TwitterCreator, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterCreator, shortCodeContext)) }); } @@ -263,23 +273,29 @@ public override async Task DisplayAsync(ContentItem contentItem, _resourceManager.RegisterMeta(new MetaEntry { Name = "twitter:url", - Content = await RenderAsync(aspect.TwitterUrl, contentItem) + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterUrl, shortCodeContext)) }); } if (!String.IsNullOrEmpty(aspect.GoogleSchema)) { - _resourceManager.RegisterHeadScript(new HtmlString($"")); + var json = await _shortcodeService.ProcessAsync(aspect.GoogleSchema, shortCodeContext); + + try + { + // Validate json format + JsonDocument.Parse(json); + } + catch + { + json = "{ \"error\": \"Invalid JSON content in SEO settings\" }"; + } + + _resourceManager.RegisterHeadScript(new HtmlString($"")); + } return null; } - - private ValueTask RenderAsync(string template, ContentItem contentItem) - => _shortcodeService.ProcessAsync(template, - new Context - { - ["ContentItem"] = contentItem - }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Controllers/AdminController.cs index dc52159673b..b72d6795bf9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Controllers/AdminController.cs @@ -11,6 +11,7 @@ using Newtonsoft.Json; using OrchardCore.DisplayManagement; using OrchardCore.DisplayManagement.Notify; +using OrchardCore.Infrastructure.Html; using OrchardCore.Liquid; using OrchardCore.Modules; using OrchardCore.Navigation; @@ -33,6 +34,7 @@ public class AdminController : Controller private readonly INotifier _notifier; private readonly IStringLocalizer S; private readonly IHtmlLocalizer H; + private readonly IHtmlSanitizerService _htmlSanitizerService; private readonly dynamic New; public AdminController( @@ -43,7 +45,8 @@ public class AdminController : Controller INotifier notifier, IShapeFactory shapeFactory, IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer + IHtmlLocalizer htmlLocalizer, + IHtmlSanitizerService htmlSanitizerService ) { _authorizationService = authorizationService; @@ -54,6 +57,7 @@ IHtmlLocalizer htmlLocalizer New = shapeFactory; S = stringLocalizer; H = htmlLocalizer; + _htmlSanitizerService = htmlSanitizerService; } public async Task Index(ContentOptions options, PagerParameters pagerParameters) @@ -159,7 +163,7 @@ public async Task CreatePost(ShortcodeTemplateViewModel model, st { Content = model.Content, Hint = model.Hint, - Usage = model.Usage, + Usage = _htmlSanitizerService.Sanitize(model.Usage), DefaultValue = model.DefaultValue, Categories = JsonConvert.DeserializeObject(model.SelectedCategories) }; @@ -256,7 +260,7 @@ public async Task Edit(string sourceName, ShortcodeTemplateViewMo { Content = model.Content, Hint = model.Hint, - Usage = model.Usage, + Usage = _htmlSanitizerService.Sanitize(model.Usage), DefaultValue = model.DefaultValue, Categories = JsonConvert.DeserializeObject(model.SelectedCategories) }; diff --git a/src/OrchardCore.Modules/OrchardCore.Tenants/Controllers/FeatureProfilesController.cs b/src/OrchardCore.Modules/OrchardCore.Tenants/Controllers/FeatureProfilesController.cs index 147ff6c33e1..c66e14cb2ef 100644 --- a/src/OrchardCore.Modules/OrchardCore.Tenants/Controllers/FeatureProfilesController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Tenants/Controllers/FeatureProfilesController.cs @@ -40,8 +40,8 @@ public class FeatureProfilesController : Controller ISiteService siteService, INotifier notifier, IShapeFactory shapeFactory, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer ) { _authorizationService = authorizationService;