From bc66cdb36c03c2aa0a5a746d511918f293e757a9 Mon Sep 17 00:00:00 2001 From: Hermes Sbicego Date: Thu, 8 Dec 2016 21:48:25 +0100 Subject: [PATCH] [Fixes #7413] Refactor taxonomies and terms permissions (#7456) Fixes #7413 * Taxonomies: - AdminController: Added Actions to AdminController in order to manage Create/Edit: Only Users with Permission.CreateTaxonomy can Edit/Create a Taxonomy - Views/Admin/Index.cshtml: Conditional display of ActionLink Create/Edit in Admin/Index.cshtml based on Permission.CreateTaxonomy - Views/Admin/Index.cshtml: Conditional display of ActionLink Delete in Admin/Index.cshtml based on Permission.ManageTaxonomies - TaxonomyPartHandler: Adding context.Metadata.EditorRouteValues for Taxonomy Terms: - Paring Action/Permission based on existing Permissions - Views/TermAdmin/Index.cshtml: Conditional display of ActionLink Create/Edit/Move/Delete based on right Permissions - TermPartHandler: Adding context.Metadata.EditorRouteValues for Taxonomy AdminMenu: - The AdminMenu was shown only if a User had ManageTaxonomies Permission; In that way, Roles who have the Create/Edit/Delete Term Permission could not access Index/Edit forms: specializing permissions on Navigation grants access to users belonging to those Roles. * Removed redundant check over SiteOwner permission --- .../Modules/Orchard.Taxonomies/AdminMenu.cs | 17 +- .../Controllers/AdminController.cs | 156 +++++++++++++++++- .../Controllers/TermAdminController.cs | 20 +-- .../Handlers/TaxonomyPartHandler.cs | 15 ++ .../Handlers/TermPartHandler.cs | 16 ++ .../Views/Admin/Index.cshtml | 25 ++- .../Views/TermAdmin/Index.cshtml | 38 +++-- 7 files changed, 247 insertions(+), 40 deletions(-) diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/AdminMenu.cs b/src/Orchard.Web/Modules/Orchard.Taxonomies/AdminMenu.cs index debd0dfa4..40ab8f9a8 100644 --- a/src/Orchard.Web/Modules/Orchard.Taxonomies/AdminMenu.cs +++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/AdminMenu.cs @@ -1,17 +1,28 @@ using Orchard.Localization; +using Orchard.Security; using Orchard.UI.Navigation; namespace Orchard.Taxonomies { public class AdminMenu : INavigationProvider { + private readonly IOrchardServices _services; + public AdminMenu(IOrchardServices services) { + _services = services; + } public Localizer T { get; set; } public string MenuName { get { return "admin"; } } public void GetNavigation(NavigationBuilder builder) { builder .AddImageSet("taxonomies") - .Add(T("Taxonomies"), "4", menu => menu - .Add(T("Manage Taxonomies"), "1.0", item => item.Action("Index", "Admin", new { area = "Orchard.Taxonomies" }).Permission(Permissions.ManageTaxonomies)) - ); + .Add(T("Taxonomies"), "4", BuildMenu); + } + + private void BuildMenu(NavigationItemBuilder menu) { + + if (_services.Authorizer.Authorize(Permissions.MergeTerms) || _services.Authorizer.Authorize(Permissions.EditTerm) || _services.Authorizer.Authorize(Permissions.CreateTerm) || _services.Authorizer.Authorize(Permissions.DeleteTerm)) { + menu.Add(T("Manage Taxonomies"), "1.0", item => item.Action("Index", "Admin", new { area = "Orchard.Taxonomies" })); + } } } + } diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/AdminController.cs index 394c58ebf..f8c0d1e70 100644 --- a/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/AdminController.cs @@ -12,6 +12,14 @@ using Orchard.Taxonomies.Services; using Orchard.UI.Navigation; using Orchard.UI.Notify; using Orchard.DisplayManagement; +using Orchard.ContentManagement.MetaData; +using Orchard.Data; +using Orchard.ContentManagement.Aspects; +using Orchard.Core.Contents.Settings; +using Orchard.Mvc.Html; +using Orchard.Utility.Extensions; +using Orchard.Mvc.Extensions; +using System.Web.Routing; namespace Orchard.Taxonomies.Controllers { @@ -19,16 +27,24 @@ namespace Orchard.Taxonomies.Controllers { public class AdminController : Controller, IUpdateModel { private readonly ITaxonomyService _taxonomyService; private readonly ISiteService _siteService; - + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ITransactionManager _transactionManager; + private readonly IContentManager _contentManager; + private string contentType = "Taxonomy"; public AdminController( IOrchardServices services, + IContentManager contentManager, + IContentDefinitionManager contentDefinitionManager, + ITransactionManager transactionManager, ITaxonomyService taxonomyService, ISiteService siteService, IShapeFactory shapeFactory) { Services = services; _siteService = siteService; _taxonomyService = taxonomyService; - + _contentDefinitionManager = contentDefinitionManager; + _transactionManager = transactionManager; + _contentManager = contentManager; T = NullLocalizer.Instance; Shape = shapeFactory; } @@ -83,6 +99,142 @@ namespace Orchard.Taxonomies.Controllers { return RedirectToAction("Index"); } + + public ActionResult Create() { + var contentItem = _contentManager.New(contentType); + + if (!Services.Authorizer.Authorize(Permissions.CreateTaxonomy, contentItem, T("Cannot create taxonomies"))) + return new HttpUnauthorizedResult(); + var model = _contentManager.BuildEditor(contentItem); + return View(model); + } + + [HttpPost, ActionName("Create")] + [Mvc.FormValueRequired("submit.Save")] + public ActionResult CreatePOST(string returnUrl) { + return CreatePOST(returnUrl, contentItem => { + if (!contentItem.Has() && !contentItem.TypeDefinition.Settings.GetModel().Draftable) + _contentManager.Publish(contentItem); + }); + } + + [HttpPost, ActionName("Create")] + [Mvc.FormValueRequired("submit.Publish")] + public ActionResult CreateAndPublishPOST(string returnUrl) { + + // pass a dummy content to the authorization check to check for "own" variations + var dummyContent = _contentManager.New(contentType); + + if (!Services.Authorizer.Authorize(Permissions.CreateTaxonomy, dummyContent, T("Couldn't create taxonomies"))) + return new HttpUnauthorizedResult(); + + return CreatePOST(returnUrl, contentItem => _contentManager.Publish(contentItem)); + } + + private ActionResult CreatePOST(string returnUrl, Action conditionallyPublish) { + var contentItem = _contentManager.New(contentType); + + if (!Services.Authorizer.Authorize(Permissions.CreateTaxonomy, contentItem, T("Couldn't create taxonomies"))) + return new HttpUnauthorizedResult(); + + _contentManager.Create(contentItem, VersionOptions.Draft); + + var model = _contentManager.UpdateEditor(contentItem, this); + + if (!ModelState.IsValid) { + _transactionManager.Cancel(); + return View(model); + } + + conditionallyPublish(contentItem); + + Services.Notifier.Information(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName) + ? T("Your content has been created.") + : T("Your {0} has been created.", contentItem.TypeDefinition.DisplayName)); + if (!string.IsNullOrEmpty(returnUrl)) { + return this.RedirectLocal(returnUrl); + } + var adminRouteValues = _contentManager.GetItemMetadata(contentItem).AdminRouteValues; + return RedirectToRoute(adminRouteValues); + } + + public ActionResult Edit(int id) { + var contentItem = _contentManager.Get(id, VersionOptions.Latest); + + if (contentItem == null) + return HttpNotFound(); + + if (!Services.Authorizer.Authorize(Permissions.CreateTaxonomy, contentItem, T("Cannot edit content"))) + return new HttpUnauthorizedResult(); + + var model = _contentManager.BuildEditor(contentItem); + return View(model); + } + + [HttpPost, ActionName("Edit")] + [Mvc.FormValueRequired("submit.Save")] + public ActionResult EditPOST(int id, string returnUrl) { + return EditPOST(id, returnUrl, contentItem => { + if (!contentItem.Has() && !contentItem.TypeDefinition.Settings.GetModel().Draftable) + _contentManager.Publish(contentItem); + }); + } + + [HttpPost, ActionName("Edit")] + [Mvc.FormValueRequired("submit.Publish")] + public ActionResult EditAndPublishPOST(int id, string returnUrl) { + var content = _contentManager.Get(id, VersionOptions.Latest); + + if (content == null) + return HttpNotFound(); + + if (!Services.Authorizer.Authorize(Permissions.CreateTaxonomy, content, T("Couldn't publish content"))) + return new HttpUnauthorizedResult(); + + return EditPOST(id, returnUrl, contentItem => _contentManager.Publish(contentItem)); + } + + private ActionResult EditPOST(int id, string returnUrl, Action conditionallyPublish) { + var contentItem = _contentManager.Get(id, VersionOptions.DraftRequired); + + if (contentItem == null) + return HttpNotFound(); + + if (!Services.Authorizer.Authorize(Permissions.CreateTaxonomy, contentItem, T("Couldn't edit content"))) + return new HttpUnauthorizedResult(); + + string previousRoute = null; + if (contentItem.Has() + && !string.IsNullOrWhiteSpace(returnUrl) + && Request.IsLocalUrl(returnUrl) + // only if the original returnUrl is the content itself + && String.Equals(returnUrl, Url.ItemDisplayUrl(contentItem), StringComparison.OrdinalIgnoreCase) + ) { + previousRoute = contentItem.As().Path; + } + + var model = _contentManager.UpdateEditor(contentItem, this); + if (!ModelState.IsValid) { + _transactionManager.Cancel(); + return View("Edit", model); + } + + conditionallyPublish(contentItem); + + if (!string.IsNullOrWhiteSpace(returnUrl) + && previousRoute != null + && !String.Equals(contentItem.As().Path, previousRoute, StringComparison.OrdinalIgnoreCase)) { + returnUrl = Url.ItemDisplayUrl(contentItem); + } + + Services.Notifier.Information(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName) + ? T("Your content has been saved.") + : T("Your {0} has been saved.", contentItem.TypeDefinition.DisplayName)); + + return this.RedirectLocal(returnUrl, () => RedirectToAction("Edit", new RouteValueDictionary { { "Id", contentItem.Id } })); + } + + [HttpPost] public ActionResult Delete(int id) { if (!Services.Authorizer.Authorize(Permissions.ManageTaxonomies, T("Couldn't delete taxonomy"))) diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/TermAdminController.cs b/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/TermAdminController.cs index 9b12af703..d8cfb8b13 100644 --- a/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/TermAdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/TermAdminController.cs @@ -76,7 +76,7 @@ namespace Orchard.Taxonomies.Controllers { Services.Notifier.Information(T("No action selected.")); break; case TermsAdminIndexBulkAction.Delete: - if (!Services.Authorizer.Authorize(Permissions.ManageTerms, T("Couldn't delete term"))) + if (!Services.Authorizer.Authorize(Permissions.DeleteTerm, T("Couldn't delete term"))) return new HttpUnauthorizedResult(); if(!checkedEntries.Any()) { @@ -93,7 +93,7 @@ namespace Orchard.Taxonomies.Controllers { break; case TermsAdminIndexBulkAction.Merge: - if (!Services.Authorizer.Authorize(Permissions.ManageTerms, T("Couldn't delete term"))) + if (!Services.Authorizer.Authorize(Permissions.ManageTerms, T("Couldn't merge term"))) return new HttpUnauthorizedResult(); return RedirectToAction("Merge", new { @@ -101,7 +101,7 @@ namespace Orchard.Taxonomies.Controllers { termIds = string.Join(",", checkedEntries.Select(o => o.Id)) }); case TermsAdminIndexBulkAction.Move: - if (!Services.Authorizer.Authorize(Permissions.ManageTerms, T("Couldn't move terms"))) + if (!Services.Authorizer.Authorize(Permissions.EditTerm, T("Couldn't move terms"))) return new HttpUnauthorizedResult(); return RedirectToAction("MoveTerm", new { @@ -130,19 +130,19 @@ namespace Orchard.Taxonomies.Controllers { return View(model); } - return RedirectToAction("Create", new { taxonomyId, parentTermId = -1 }); + return RedirectToAction("Create", new { taxonomyId, parentTermId = -1, ReturnUrl = Url.Action("Index", new { taxonomyId = taxonomyId }) }); } [HttpPost] public ActionResult SelectTerm(int taxonomyId, int selectedTermId) { - if (!Services.Authorizer.Authorize(Permissions.ManageTerms, T("Not allowed to select terms"))) + if (!Services.Authorizer.Authorize(Permissions.CreateTerm, T("Not allowed to select terms"))) return new HttpUnauthorizedResult(); - return RedirectToAction("Create", new { taxonomyId, parentTermId = selectedTermId }); + return RedirectToAction("Create", new { taxonomyId, parentTermId = selectedTermId, ReturnUrl = Url.Action("Index", new { taxonomyId = taxonomyId }) }); } public ActionResult MoveTerm(int taxonomyId, string termIds) { - if (!Services.Authorizer.Authorize(Permissions.CreateTerm, T("Not allowed to move terms"))) + if (!Services.Authorizer.Authorize(Permissions.EditTerm, T("Not allowed to move terms"))) return new HttpUnauthorizedResult(); var terms = ResolveTermIds(termIds); @@ -164,7 +164,7 @@ namespace Orchard.Taxonomies.Controllers { [HttpPost] public ActionResult MoveTerm(int taxonomyId, int selectedTermId, string termIds) { - if (!Services.Authorizer.Authorize(Permissions.ManageTerms, T("Not allowed to move terms"))) + if (!Services.Authorizer.Authorize(Permissions.EditTerm, T("Not allowed to move terms"))) return new HttpUnauthorizedResult(); var taxonomy = _taxonomyService.GetTaxonomy(taxonomyId); @@ -217,7 +217,7 @@ namespace Orchard.Taxonomies.Controllers { public ActionResult Edit(int id) { - if (!Services.Authorizer.Authorize(Permissions.ManageTerms, T("Not allowed to manage terms"))) + if (!Services.Authorizer.Authorize(Permissions.EditTerm, T("Not allowed to edit terms"))) return new HttpUnauthorizedResult(); var term = _taxonomyService.GetTerm(id); @@ -230,7 +230,7 @@ namespace Orchard.Taxonomies.Controllers { [HttpPost, ActionName("Edit")] public ActionResult EditPost(int id) { - if (!Services.Authorizer.Authorize(Permissions.ManageTerms, T("Couldn't edit term"))) + if (!Services.Authorizer.Authorize(Permissions.EditTerm, T("Couldn't edit term"))) return new HttpUnauthorizedResult(); var term = _taxonomyService.GetTerm(id); diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/Handlers/TaxonomyPartHandler.cs b/src/Orchard.Web/Modules/Orchard.Taxonomies/Handlers/TaxonomyPartHandler.cs index 3cd2fef4b..c3eca356b 100644 --- a/src/Orchard.Web/Modules/Orchard.Taxonomies/Handlers/TaxonomyPartHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/Handlers/TaxonomyPartHandler.cs @@ -8,6 +8,7 @@ using Orchard.ContentManagement.Handlers; using Orchard.Data; using Orchard.Taxonomies.Settings; using System; +using System.Web.Routing; namespace Orchard.Taxonomies.Handlers { public class TaxonomyPartHandler : ContentHandler { @@ -51,5 +52,19 @@ namespace Orchard.Taxonomies.Handlers { } }); } + protected override void GetItemMetadata(GetContentItemMetadataContext context) { + var taxonomy = context.ContentItem.As(); + + if (taxonomy == null) + return; + + context.Metadata.EditorRouteValues = new RouteValueDictionary { + {"Area", "Orchard.Taxonomies"}, + {"Controller", "Admin"}, + {"Action", "Edit"}, + {"Id", taxonomy.Id} + }; + + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/Handlers/TermPartHandler.cs b/src/Orchard.Web/Modules/Orchard.Taxonomies/Handlers/TermPartHandler.cs index 587783543..34de4fe93 100644 --- a/src/Orchard.Web/Modules/Orchard.Taxonomies/Handlers/TermPartHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/Handlers/TermPartHandler.cs @@ -1,6 +1,8 @@ using Orchard.Taxonomies.Models; using Orchard.ContentManagement.Handlers; using Orchard.Data; +using System.Web.Routing; +using Orchard.ContentManagement; namespace Orchard.Taxonomies.Handlers { public class TermPartHandler : ContentHandler { @@ -8,5 +10,19 @@ namespace Orchard.Taxonomies.Handlers { Filters.Add(StorageFilter.For(repository)); OnInitializing((context, part) => part.Selectable = true); } + protected override void GetItemMetadata(GetContentItemMetadataContext context) { + var term = context.ContentItem.As(); + + if (term == null) + return; + + context.Metadata.EditorRouteValues = new RouteValueDictionary { + {"Area", "Orchard.Taxonomies"}, + {"Controller", "TermAdmin"}, + {"Action", "Edit"}, + {"Id", term.Id} + }; + + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/Views/Admin/Index.cshtml b/src/Orchard.Web/Modules/Orchard.Taxonomies/Views/Admin/Index.cshtml index cdd972f2c..faa351f19 100644 --- a/src/Orchard.Web/Modules/Orchard.Taxonomies/Views/Admin/Index.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/Views/Admin/Index.cshtml @@ -1,4 +1,5 @@ @model TaxonomyAdminIndexViewModel +@using Orchard.Taxonomies; @using Orchard.Taxonomies.ViewModels; @{ @@ -16,7 +17,9 @@ -
@Html.ActionLink(T("Add a taxonomy").Text, "Create", new { Area = "Contents", Id = "Taxonomy", ReturnUrl = Request.RawUrl }, new { @class = "button primaryAction" })
+ if (Authorizer.Authorize(Permissions.CreateTaxonomy)) { +
@Html.ActionLink(T("Add a taxonomy").Text, "Create", new { Area = "Orchard.Taxonomies", ReturnUrl = Request.RawUrl }, new { @class = "button primaryAction" })
+ }
@@ -36,12 +39,12 @@ - taxonomyIndex++; + taxonomyIndex++; }
- @if (!taxonomyEntry.IsInternal || Authorizer.Authorize(Orchard.Security.StandardPermissions.SiteOwner)) { + @if (!taxonomyEntry.IsInternal || Authorizer.Authorize(Permissions.ManageTaxonomies)) { } - @if (!taxonomyEntry.IsInternal || Authorizer.Authorize(Orchard.Security.StandardPermissions.SiteOwner)) { + @if (!taxonomyEntry.IsInternal || Authorizer.Authorize(Permissions.CreateTaxonomy)) { @Html.ActionLink(taxonomyEntry.Name, "Index", "TermAdmin", new { taxonomyId = taxonomyEntry.Id }, new object { }) } else { @@ -49,15 +52,21 @@ } - @if (!taxonomyEntry.IsInternal || Authorizer.Authorize(Orchard.Security.StandardPermissions.SiteOwner)) { - @Html.ItemEditLink(T("Edit").Text, taxonomyEntry.ContentItem) | + @if (!taxonomyEntry.IsInternal) { + if (Authorizer.Authorize(Permissions.CreateTaxonomy)) { + @Html.ItemEditLink(T("Edit").Text, taxonomyEntry.ContentItem, new { ReturnUrl = Request.RawUrl }) | + } @Html.ActionLink(T("Terms").Text, "Index", "TermAdmin", new { taxonomyId = taxonomyEntry.Id }, new object { }) | - @Html.ActionLink(T("Delete").Text, "Delete", new { id = taxonomyEntry.Id }, new { itemprop = "RemoveUrl UnsafeUrl" }) | - @Html.ActionLink(T("Import").Text, "Import", new { id = taxonomyEntry.Id }, new object { }) + if (Authorizer.Authorize(Permissions.ManageTaxonomies)) { + @Html.ActionLink(T("Delete").Text, "Delete", new { id = taxonomyEntry.Id }, new { itemprop = "RemoveUrl UnsafeUrl" }) | + } + if (Authorizer.Authorize(Permissions.CreateTaxonomy)) { + @Html.ActionLink(T("Import").Text, "Import", new { id = taxonomyEntry.Id }, new object { }) + } }
@Display(Model.Pager) diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/Views/TermAdmin/Index.cshtml b/src/Orchard.Web/Modules/Orchard.Taxonomies/Views/TermAdmin/Index.cshtml index 925eb8190..1185666bd 100644 --- a/src/Orchard.Web/Modules/Orchard.Taxonomies/Views/TermAdmin/Index.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/Views/TermAdmin/Index.cshtml @@ -1,6 +1,6 @@ @model TermAdminIndexViewModel +@using Orchard.Taxonomies @using Orchard.Taxonomies.ViewModels; - @using Orchard.Taxonomies.Helpers; @{ Style.Include("admin-taxonomy.css"); @@ -9,9 +9,9 @@ var termIndex = 0; } -@using(Html.BeginFormAntiForgeryPost()) { +@using (Html.BeginFormAntiForgeryPost()) { @Html.ValidationSummary() - @Html.HiddenFor(m=>m.TaxonomyId) + @Html.HiddenFor(m => m.TaxonomyId)
-
@Html.ActionLink(T("Add a term").ToString(), "SelectTerm", new { taxonomyId = Model.Taxonomy.Id }, new { @class = "button primaryAction" })
+ if (Authorizer.Authorize(Permissions.CreateTerm)) { +
@Html.ActionLink(T("Add a term").ToString(), "SelectTerm", new { taxonomyId = Model.Taxonomy.Id }, new { @class = "button primaryAction" })
+ }
@@ -34,20 +36,22 @@ - @foreach ( var termEntry in Model.Terms) { + @foreach (var termEntry in Model.Terms) { var ti = termIndex; - - - - + + + + termIndex++; }
- - @* Tabs for levels *@ @for ( var i = 1; i <= termEntry.GetLevels(); i++ ) {   } - - @Html.ItemDisplayLink(termEntry.Name, termEntry.ContentItem) - - @Html.ItemEditLink(T("Edit").Text, termEntry.ContentItem, new { returnUrl = Url.Action("Index", "TermAdmin", new { taxonomyId = Model.Taxonomy.Id }) }) | - @Html.ActionLink(T("Move").ToString(), "MoveTerm", new { taxonomyId = Model.Taxonomy.Id, termIds = termEntry.Id }) -
+ + @* Tabs for levels *@ @for (var i = 1; i <= termEntry.GetLevels(); i++) {   } + + @Html.ItemDisplayLink(termEntry.Name, termEntry.ContentItem) + + @if (Authorizer.Authorize(Permissions.EditTerm)) { + @Html.ItemEditLink(T("Edit").Text, termEntry.ContentItem, new { returnUrl = Url.Action("Index", "TermAdmin", new { taxonomyId = Model.Taxonomy.Id }) }) | + @Html.ActionLink(T("Move").ToString(), "MoveTerm", new { taxonomyId = Model.Taxonomy.Id, termIds = termEntry.Id }) + } +