mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-09-23 12:53:33 +08:00
Extending security model for owned/other content permission support
Added an IContent parameter to IAuthorizationService and IAuthorizer Added an IAuthorizationServiceEvents interface for modules to participate in the process Updated the role-based authorization service to fire events and recheck access based on adjustments Added hook in Orchard.Pages/Security/Authorization.cs with "pages" specific auth adjustments --HG-- extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4045861
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using Orchard.ContentManagement;
|
||||||
using Orchard.Security;
|
using Orchard.Security;
|
||||||
using Orchard.Security.Permissions;
|
using Orchard.Security.Permissions;
|
||||||
using Orchard.UI.Navigation;
|
using Orchard.UI.Navigation;
|
||||||
@@ -19,10 +20,10 @@ namespace Orchard.Tests.UI.Navigation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class StubAuth : IAuthorizationService {
|
public class StubAuth : IAuthorizationService {
|
||||||
public void CheckAccess(IUser user, Permission permission) {
|
public void CheckAccess(Permission permission, IUser user, IContent content) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryCheckAccess(IUser user, Permission permission) {
|
public bool TryCheckAccess(Permission permission, IUser user, IContent content) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -134,7 +134,7 @@ namespace Orchard.Core.Common.Providers {
|
|||||||
|
|
||||||
private void GetEditor(BuildEditorModelContext context, CommonAspect instance) {
|
private void GetEditor(BuildEditorModelContext context, CommonAspect instance) {
|
||||||
var currentUser = _authenticationService.GetAuthenticatedUser();
|
var currentUser = _authenticationService.GetAuthenticatedUser();
|
||||||
if (!_authorizationService.TryCheckAccess(currentUser, Permissions.ChangeOwner)) {
|
if (!_authorizationService.TryCheckAccess(Permissions.ChangeOwner, currentUser, instance)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var viewModel = new OwnerEditorViewModel();
|
var viewModel = new OwnerEditorViewModel();
|
||||||
@@ -152,7 +152,7 @@ namespace Orchard.Core.Common.Providers {
|
|||||||
instance.VersionModifiedUtc = _clock.UtcNow;
|
instance.VersionModifiedUtc = _clock.UtcNow;
|
||||||
|
|
||||||
var currentUser = _authenticationService.GetAuthenticatedUser();
|
var currentUser = _authenticationService.GetAuthenticatedUser();
|
||||||
if (!_authorizationService.TryCheckAccess(currentUser, Permissions.ChangeOwner)) {
|
if (!_authorizationService.TryCheckAccess(Permissions.ChangeOwner, currentUser, instance)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -46,7 +46,7 @@ namespace Orchard.Media.Services {
|
|||||||
XRpcStruct file) {
|
XRpcStruct file) {
|
||||||
|
|
||||||
var user = _membershipService.ValidateUser(userName, password);
|
var user = _membershipService.ValidateUser(userName, password);
|
||||||
if (!_authorizationService.TryCheckAccess(user, Permissions.UploadMediaFiles)) {
|
if (!_authorizationService.TryCheckAccess(Permissions.UploadMediaFiles, user, null)) {
|
||||||
//TEMP: return appropriate access-denied response for user
|
//TEMP: return appropriate access-denied response for user
|
||||||
throw new ApplicationException("Access denied");
|
throw new ApplicationException("Access denied");
|
||||||
}
|
}
|
||||||
|
@@ -14,15 +14,15 @@ using Orchard.UI.Notify;
|
|||||||
namespace Orchard.Pages.Controllers {
|
namespace Orchard.Pages.Controllers {
|
||||||
[ValidateInput(false)]
|
[ValidateInput(false)]
|
||||||
public class AdminController : Controller, IUpdateModel {
|
public class AdminController : Controller, IUpdateModel {
|
||||||
private readonly IOrchardServices _services;
|
|
||||||
private readonly IPageService _pageService;
|
private readonly IPageService _pageService;
|
||||||
|
|
||||||
public AdminController(IOrchardServices services, IPageService pageService) {
|
public AdminController(IOrchardServices services, IPageService pageService) {
|
||||||
_services = services;
|
Services = services;
|
||||||
_pageService = pageService;
|
_pageService = pageService;
|
||||||
T = NullLocalizer.Instance;
|
T = NullLocalizer.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IOrchardServices Services { get; private set; }
|
||||||
private Localizer T { get; set; }
|
private Localizer T { get; set; }
|
||||||
|
|
||||||
public ActionResult List(PagesOptions options) {
|
public ActionResult List(PagesOptions options) {
|
||||||
@@ -58,7 +58,7 @@ namespace Orchard.Pages.Controllers {
|
|||||||
case PagesBulkAction.None:
|
case PagesBulkAction.None:
|
||||||
break;
|
break;
|
||||||
case PagesBulkAction.PublishNow:
|
case PagesBulkAction.PublishNow:
|
||||||
if (!_services.Authorizer.Authorize(Permissions.PublishPages, T("Couldn't publish page")))
|
if (!Services.Authorizer.Authorize(Permissions.PublishPages, T("Couldn't publish page")))
|
||||||
return new HttpUnauthorizedResult();
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
foreach (PageEntry entry in checkedEntries) {
|
foreach (PageEntry entry in checkedEntries) {
|
||||||
@@ -67,7 +67,7 @@ namespace Orchard.Pages.Controllers {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PagesBulkAction.Unpublish:
|
case PagesBulkAction.Unpublish:
|
||||||
if (!_services.Authorizer.Authorize(Permissions.PublishPages, T("Couldn't unpublish page")))
|
if (!Services.Authorizer.Authorize(Permissions.PublishPages, T("Couldn't unpublish page")))
|
||||||
return new HttpUnauthorizedResult();
|
return new HttpUnauthorizedResult();
|
||||||
foreach (PageEntry entry in checkedEntries) {
|
foreach (PageEntry entry in checkedEntries) {
|
||||||
var page = _pageService.GetLatest(entry.PageId);
|
var page = _pageService.GetLatest(entry.PageId);
|
||||||
@@ -75,7 +75,7 @@ namespace Orchard.Pages.Controllers {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PagesBulkAction.Delete:
|
case PagesBulkAction.Delete:
|
||||||
if (!_services.Authorizer.Authorize(Permissions.DeletePages, T("Couldn't delete page")))
|
if (!Services.Authorizer.Authorize(Permissions.DeletePages, T("Couldn't delete page")))
|
||||||
return new HttpUnauthorizedResult();
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
foreach (PageEntry entry in checkedEntries) {
|
foreach (PageEntry entry in checkedEntries) {
|
||||||
@@ -99,10 +99,10 @@ namespace Orchard.Pages.Controllers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ActionResult Create() {
|
public ActionResult Create() {
|
||||||
if (!_services.Authorizer.Authorize(Permissions.EditPages, T("Not allowed to create a page")))
|
if (!Services.Authorizer.Authorize(Permissions.EditPages, T("Not allowed to create a page")))
|
||||||
return new HttpUnauthorizedResult();
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
var page = _services.ContentManager.BuildEditorModel(_services.ContentManager.New<Page>("page"));
|
var page = Services.ContentManager.BuildEditorModel(Services.ContentManager.New<Page>("page"));
|
||||||
|
|
||||||
var model = new PageCreateViewModel {
|
var model = new PageCreateViewModel {
|
||||||
Page = page
|
Page = page
|
||||||
@@ -113,7 +113,7 @@ namespace Orchard.Pages.Controllers {
|
|||||||
|
|
||||||
[HttpPost, ActionName("Create")]
|
[HttpPost, ActionName("Create")]
|
||||||
public ActionResult CreatePOST(PageCreateViewModel model) {
|
public ActionResult CreatePOST(PageCreateViewModel model) {
|
||||||
if (!_services.Authorizer.Authorize(Permissions.EditPages, T("Couldn't create page")))
|
if (!Services.Authorizer.Authorize(Permissions.EditPages, T("Couldn't create page")))
|
||||||
return new HttpUnauthorizedResult();
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
//TODO: (erikpo) Move this duplicate code somewhere else
|
//TODO: (erikpo) Move this duplicate code somewhere else
|
||||||
@@ -129,38 +129,37 @@ namespace Orchard.Pages.Controllers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
model.Page = _services.ContentManager.UpdateEditorModel(_services.ContentManager.New<Page>("page"), this);
|
model.Page = Services.ContentManager.UpdateEditorModel(Services.ContentManager.New<Page>("page"), this);
|
||||||
if (!publishNow && publishDate != null)
|
if (!publishNow && publishDate != null)
|
||||||
model.Page.Item.Published = publishDate.Value;
|
model.Page.Item.Published = publishDate.Value;
|
||||||
|
|
||||||
if (!ModelState.IsValid) {
|
if (!ModelState.IsValid) {
|
||||||
_services.TransactionManager.Cancel();
|
Services.TransactionManager.Cancel();
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
_services.ContentManager.Create(model.Page.Item.ContentItem, publishNow ? VersionOptions.Published : VersionOptions.Draft);
|
Services.ContentManager.Create(model.Page.Item.ContentItem, publishNow ? VersionOptions.Published : VersionOptions.Draft);
|
||||||
|
|
||||||
if (publishNow)
|
if (publishNow)
|
||||||
_services.Notifier.Information(T("Page has been published"));
|
Services.Notifier.Information(T("Page has been published"));
|
||||||
else if (publishDate != null)
|
else if (publishDate != null)
|
||||||
_services.Notifier.Information(T("Page has been scheduled for publishing"));
|
Services.Notifier.Information(T("Page has been scheduled for publishing"));
|
||||||
else
|
else
|
||||||
_services.Notifier.Information(T("Page draft has been saved"));
|
Services.Notifier.Information(T("Page draft has been saved"));
|
||||||
|
|
||||||
return RedirectToAction("Edit", "Admin", new { id = model.Page.Item.ContentItem.Id });
|
return RedirectToAction("Edit", "Admin", new { id = model.Page.Item.ContentItem.Id });
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActionResult Edit(int id) {
|
public ActionResult Edit(int id) {
|
||||||
if (!_services.Authorizer.Authorize(Permissions.EditPages, T("Couldn't edit page")))
|
|
||||||
return new HttpUnauthorizedResult();
|
|
||||||
|
|
||||||
Page page = _pageService.GetLatest(id);
|
Page page = _pageService.GetLatest(id);
|
||||||
|
|
||||||
if (page == null)
|
if (page == null)
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
|
|
||||||
|
if (!Services.Authorizer.Authorize(Permissions.EditOthersPages, page, T("Couldn't edit page")))
|
||||||
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
var model = new PageEditViewModel {
|
var model = new PageEditViewModel {
|
||||||
Page = _services.ContentManager.BuildEditorModel(page)
|
Page = Services.ContentManager.BuildEditorModel(page)
|
||||||
};
|
};
|
||||||
|
|
||||||
return View(model);
|
return View(model);
|
||||||
@@ -168,14 +167,14 @@ namespace Orchard.Pages.Controllers {
|
|||||||
|
|
||||||
[HttpPost, ActionName("Edit")]
|
[HttpPost, ActionName("Edit")]
|
||||||
public ActionResult EditPOST(int id) {
|
public ActionResult EditPOST(int id) {
|
||||||
if (!_services.Authorizer.Authorize(Permissions.EditPages, T("Couldn't edit page")))
|
|
||||||
return new HttpUnauthorizedResult();
|
|
||||||
|
|
||||||
Page page = _pageService.GetPageOrDraft(id);
|
Page page = _pageService.GetPageOrDraft(id);
|
||||||
|
|
||||||
if (page == null)
|
if (page == null)
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
|
|
||||||
|
if (!Services.Authorizer.Authorize(Permissions.EditOthersPages, page, T("Couldn't edit page")))
|
||||||
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
|
|
||||||
//TODO: (erikpo) Move this duplicate code somewhere else
|
//TODO: (erikpo) Move this duplicate code somewhere else
|
||||||
DateTime? publishDate = null;
|
DateTime? publishDate = null;
|
||||||
bool publishNow = false;
|
bool publishNow = false;
|
||||||
@@ -198,40 +197,39 @@ namespace Orchard.Pages.Controllers {
|
|||||||
_pageService.Unpublish(page);
|
_pageService.Unpublish(page);
|
||||||
|
|
||||||
var model = new PageEditViewModel {
|
var model = new PageEditViewModel {
|
||||||
Page = _services.ContentManager.UpdateEditorModel(page, this)
|
Page = Services.ContentManager.UpdateEditorModel(page, this)
|
||||||
};
|
};
|
||||||
|
|
||||||
TryUpdateModel(model);
|
TryUpdateModel(model);
|
||||||
|
|
||||||
if (!ModelState.IsValid) {
|
if (!ModelState.IsValid) {
|
||||||
_services.TransactionManager.Cancel();
|
Services.TransactionManager.Cancel();
|
||||||
|
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (publishNow)
|
if (publishNow)
|
||||||
_services.Notifier.Information(T("Page has been published"));
|
Services.Notifier.Information(T("Page has been published"));
|
||||||
else if (publishDate != null)
|
else if (publishDate != null)
|
||||||
_services.Notifier.Information(T("Page has been scheduled for publishing"));
|
Services.Notifier.Information(T("Page has been scheduled for publishing"));
|
||||||
else
|
else
|
||||||
_services.Notifier.Information(T("Page draft has been saved"));
|
Services.Notifier.Information(T("Page draft has been saved"));
|
||||||
|
|
||||||
return RedirectToAction("Edit", "Admin", new { id = model.Page.Item.ContentItem.Id });
|
return RedirectToAction("Edit", "Admin", new { id = model.Page.Item.ContentItem.Id });
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public ActionResult Delete(int id) {
|
public ActionResult Delete(int id) {
|
||||||
if (!_services.Authorizer.Authorize(Permissions.DeletePages, T("Couldn't delete page")))
|
|
||||||
return new HttpUnauthorizedResult();
|
|
||||||
|
|
||||||
Page page = _pageService.Get(id);
|
Page page = _pageService.Get(id);
|
||||||
|
|
||||||
if (page == null)
|
if (page == null)
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
|
|
||||||
|
if (!Services.Authorizer.Authorize(Permissions.DeleteOthersPages, page, T("Couldn't delete page")))
|
||||||
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
_pageService.Delete(page);
|
_pageService.Delete(page);
|
||||||
|
|
||||||
_services.Notifier.Information(T("Page was successfully deleted"));
|
Services.Notifier.Information(T("Page was successfully deleted"));
|
||||||
|
|
||||||
return RedirectToAction("List");
|
return RedirectToAction("List");
|
||||||
}
|
}
|
||||||
|
@@ -82,6 +82,7 @@
|
|||||||
<Compile Include="Permissions.cs" />
|
<Compile Include="Permissions.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Routes.cs" />
|
<Compile Include="Routes.cs" />
|
||||||
|
<Compile Include="Security\Authorization.cs" />
|
||||||
<Compile Include="Services\IPageService.cs" />
|
<Compile Include="Services\IPageService.cs" />
|
||||||
<Compile Include="Services\PageService.cs" />
|
<Compile Include="Services\PageService.cs" />
|
||||||
<Compile Include="Services\SlugConstraint.cs">
|
<Compile Include="Services\SlugConstraint.cs">
|
||||||
|
@@ -0,0 +1,96 @@
|
|||||||
|
using JetBrains.Annotations;
|
||||||
|
using Orchard.ContentManagement;
|
||||||
|
using Orchard.ContentManagement.Aspects;
|
||||||
|
using Orchard.Pages.Models;
|
||||||
|
using Orchard.Security;
|
||||||
|
using Orchard.Security.Permissions;
|
||||||
|
|
||||||
|
namespace Orchard.Pages.Security {
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class Authorization : AuthorizationServiceEvents {
|
||||||
|
public override void Adjust(CheckAccessContext context) {
|
||||||
|
if (context.Granted == false &&
|
||||||
|
context.Content.Is<Page>() &&
|
||||||
|
HasOwnerVariation(context.Permission) &&
|
||||||
|
HasOwnership(context.User, context.Content)) {
|
||||||
|
|
||||||
|
context.Adjusted = true;
|
||||||
|
context.Permission = GetOwnerVariation(context.Permission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasOwnership(IUser user, IContent content) {
|
||||||
|
if (user==null || content==null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var common = content.As<ICommonAspect>();
|
||||||
|
if (common==null || common.Owner==null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return user.Id == common.Owner.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasOwnerVariation(Permission permission) {
|
||||||
|
return GetOwnerVariation(permission) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Permission GetOwnerVariation(Permission permission) {
|
||||||
|
if (permission.Name == Permissions.PublishOthersPages.Name)
|
||||||
|
return Permissions.PublishPages;
|
||||||
|
if (permission.Name == Permissions.EditOthersPages.Name)
|
||||||
|
return Permissions.EditPages;
|
||||||
|
if (permission.Name == Permissions.DeleteOthersPages.Name)
|
||||||
|
return Permissions.DeletePages;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Orchard.ContentManagement;
|
||||||
|
using Orchard.ContentManagement.Aspects;
|
||||||
|
using Orchard.Pages.Models;
|
||||||
|
using Orchard.Security;
|
||||||
|
using Orchard.Security.Permissions;
|
||||||
|
|
||||||
|
namespace Orchard.Pages.Security {
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class Authorization : AuthorizationServiceEvents {
|
||||||
|
public override void Adjust(CheckAccessContext context) {
|
||||||
|
if (context.Granted == false &&
|
||||||
|
context.Content.Is<Page>() &&
|
||||||
|
HasOwnerVariation(context.Permission) &&
|
||||||
|
HasOwnership(context.User, context.Content)) {
|
||||||
|
|
||||||
|
context.Adjusted = true;
|
||||||
|
context.Permission = GetOwnerVariation(context.Permission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasOwnership(IUser user, IContent content) {
|
||||||
|
if (user==null || content==null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var common = content.As<ICommonAspect>();
|
||||||
|
if (common==null || common.Owner==null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return user.Id == common.Owner.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasOwnerVariation(Permission permission) {
|
||||||
|
return GetOwnerVariation(permission) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Permission GetOwnerVariation(Permission permission) {
|
||||||
|
if (permission.Name == Permissions.PublishOthersPages.Name)
|
||||||
|
return Permissions.PublishPages;
|
||||||
|
if (permission.Name == Permissions.EditOthersPages.Name)
|
||||||
|
return Permissions.EditPages;
|
||||||
|
if (permission.Name == Permissions.DeleteOthersPages.Name)
|
||||||
|
return Permissions.DeletePages;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -111,7 +111,7 @@ namespace Orchard.Roles.Controllers {
|
|||||||
var simulation = UserSimulation.Create(role.Name);
|
var simulation = UserSimulation.Create(role.Name);
|
||||||
model.EffectivePermissions = model.PackagePermissions
|
model.EffectivePermissions = model.PackagePermissions
|
||||||
.SelectMany(group => group.Value)
|
.SelectMany(group => group.Value)
|
||||||
.Where(permission => _authorizationService.TryCheckAccess(simulation, permission))
|
.Where(permission => _authorizationService.TryCheckAccess(permission, simulation, null))
|
||||||
.Select(permission=>permission.Name)
|
.Select(permission=>permission.Name)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToList();
|
.ToList();
|
||||||
|
@@ -40,7 +40,7 @@ namespace Orchard.Roles.Controllers {
|
|||||||
|
|
||||||
protected override DriverResult Editor(UserRoles userRoles) {
|
protected override DriverResult Editor(UserRoles userRoles) {
|
||||||
// don't show editor without apply roles permission
|
// don't show editor without apply roles permission
|
||||||
if (!_authorizationService.TryCheckAccess(_authenticationService.GetAuthenticatedUser(), Permissions.ApplyRoles))
|
if (!_authorizationService.TryCheckAccess(Permissions.ApplyRoles, _authenticationService.GetAuthenticatedUser(), userRoles))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var roles =
|
var roles =
|
||||||
@@ -61,7 +61,7 @@ namespace Orchard.Roles.Controllers {
|
|||||||
|
|
||||||
protected override DriverResult Editor(UserRoles userRoles, IUpdateModel updater) {
|
protected override DriverResult Editor(UserRoles userRoles, IUpdateModel updater) {
|
||||||
// don't apply editor without apply roles permission
|
// don't apply editor without apply roles permission
|
||||||
if (!_authorizationService.TryCheckAccess(_authenticationService.GetAuthenticatedUser(), Permissions.ApplyRoles))
|
if (!_authorizationService.TryCheckAccess(Permissions.ApplyRoles, _authenticationService.GetAuthenticatedUser(), userRoles))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var model = new UserRolesViewModel {
|
var model = new UserRolesViewModel {
|
||||||
|
@@ -13,9 +13,11 @@ using Orchard.Settings;
|
|||||||
namespace Orchard.Roles.Services {
|
namespace Orchard.Roles.Services {
|
||||||
public class RolesBasedAuthorizationService : IAuthorizationService {
|
public class RolesBasedAuthorizationService : IAuthorizationService {
|
||||||
private readonly IRoleService _roleService;
|
private readonly IRoleService _roleService;
|
||||||
|
private readonly IEnumerable<IAuthorizationServiceEvents> _events;
|
||||||
|
|
||||||
public RolesBasedAuthorizationService(IRoleService roleService) {
|
public RolesBasedAuthorizationService(IRoleService roleService, IEnumerable<IAuthorizationServiceEvents> events) {
|
||||||
_roleService = roleService;
|
_roleService = roleService;
|
||||||
|
_events = events;
|
||||||
Logger = NullLogger.Instance;
|
Logger = NullLogger.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,37 +26,54 @@ namespace Orchard.Roles.Services {
|
|||||||
|
|
||||||
#region Implementation of IAuthorizationService
|
#region Implementation of IAuthorizationService
|
||||||
|
|
||||||
public void CheckAccess(IUser user, Permission permission) {
|
public void CheckAccess(Permission permission, IUser user, IContent content) {
|
||||||
if (!TryCheckAccess(user, permission)) {
|
if (!TryCheckAccess(permission, user, content)) {
|
||||||
throw new OrchardSecurityException { PermissionName = permission.Name };
|
throw new OrchardSecurityException { PermissionName = permission.Name };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryCheckAccess(IUser user, Permission permission) {
|
public bool TryCheckAccess(Permission permission, IUser user, IContent content) {
|
||||||
if (user == null) {
|
var context = new CheckAccessContext { Permission = permission, User = user, Content = content };
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (String.Equals(user.UserName, "Administrator", StringComparison.OrdinalIgnoreCase) ||
|
_events.Invoke(x => x.Checking(context), Logger);
|
||||||
((!String.IsNullOrEmpty(CurrentSite.SuperUser) &&
|
|
||||||
String.Equals(user.UserName, CurrentSite.SuperUser, StringComparison.OrdinalIgnoreCase)))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var grantingNames = PermissionNames(permission, Enumerable.Empty<string>()).ToArray();
|
for (var adjustmentLimiter = 0; adjustmentLimiter != 3; ++adjustmentLimiter) {
|
||||||
|
if (!context.Granted && context.User != null) {
|
||||||
IEnumerable<string> rolesForUser = user.As<IUserRoles>().Roles;
|
if (String.Equals(context.User.UserName, "Administrator", StringComparison.OrdinalIgnoreCase) ||
|
||||||
foreach (var role in rolesForUser) {
|
((!String.IsNullOrEmpty(CurrentSite.SuperUser) &&
|
||||||
RoleRecord roleRecord = _roleService.GetRoleByName(role);
|
String.Equals(context.User.UserName, CurrentSite.SuperUser, StringComparison.OrdinalIgnoreCase)))) {
|
||||||
foreach (var permissionName in _roleService.GetPermissionsForRole(roleRecord.Id)) {
|
context.Granted = true;
|
||||||
string possessedName = permissionName;
|
|
||||||
if (grantingNames.Any(grantingName => String.Equals(possessedName, grantingName, StringComparison.OrdinalIgnoreCase))) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!context.Granted && context.User != null && context.User.Has<IUserRoles>()) {
|
||||||
|
var grantingNames = PermissionNames(context.Permission, Enumerable.Empty<string>()).ToArray();
|
||||||
|
IEnumerable<string> rolesForUser = context.User.As<IUserRoles>().Roles;
|
||||||
|
|
||||||
|
foreach (var role in rolesForUser) {
|
||||||
|
RoleRecord roleRecord = _roleService.GetRoleByName(role);
|
||||||
|
foreach (var permissionName in _roleService.GetPermissionsForRole(roleRecord.Id)) {
|
||||||
|
string possessedName = permissionName;
|
||||||
|
if (grantingNames.Any(grantingName => String.Equals(possessedName, grantingName, StringComparison.OrdinalIgnoreCase))) {
|
||||||
|
context.Granted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.Granted)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.Granted)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Adjusted = false;
|
||||||
|
_events.Invoke(x => x.Adjust(context), Logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
_events.Invoke(x => x.Complete(context), Logger);
|
||||||
|
|
||||||
|
return context.Granted;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<string> PermissionNames(Permission permission, IEnumerable<string> stack) {
|
private static IEnumerable<string> PermissionNames(Permission permission, IEnumerable<string> stack) {
|
||||||
|
@@ -28,7 +28,7 @@ namespace Orchard.Tags.Controllers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override DriverResult Editor(HasTags part) {
|
protected override DriverResult Editor(HasTags part) {
|
||||||
if (!_authorizationService.TryCheckAccess(CurrentUser, Permissions.ApplyTag))
|
if (!_authorizationService.TryCheckAccess(Permissions.ApplyTag, CurrentUser, part))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var model = new EditTagsViewModel {
|
var model = new EditTagsViewModel {
|
||||||
@@ -38,7 +38,7 @@ namespace Orchard.Tags.Controllers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override DriverResult Editor(HasTags part, IUpdateModel updater) {
|
protected override DriverResult Editor(HasTags part, IUpdateModel updater) {
|
||||||
if (!_authorizationService.TryCheckAccess(CurrentUser, Permissions.ApplyTag))
|
if (!_authorizationService.TryCheckAccess(Permissions.ApplyTag, CurrentUser, part))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var model = new EditTagsViewModel();
|
var model = new EditTagsViewModel();
|
||||||
|
@@ -66,7 +66,7 @@ namespace Orchard.Tags.Services {
|
|||||||
|
|
||||||
public void CreateTag(string tagName) {
|
public void CreateTag(string tagName) {
|
||||||
if (_tagRepository.Get(x => x.TagName == tagName) == null) {
|
if (_tagRepository.Get(x => x.TagName == tagName) == null) {
|
||||||
_authorizationService.CheckAccess(CurrentUser, Permissions.CreateTag);
|
_authorizationService.CheckAccess(Permissions.CreateTag, CurrentUser, null);
|
||||||
|
|
||||||
Tag tag = new Tag { TagName = tagName };
|
Tag tag = new Tag { TagName = tagName };
|
||||||
_tagRepository.Create(tag);
|
_tagRepository.Create(tag);
|
||||||
@@ -129,7 +129,7 @@ namespace Orchard.Tags.Services {
|
|||||||
|
|
||||||
foreach (var tagContentItem in currentTagsForContentItem) {
|
foreach (var tagContentItem in currentTagsForContentItem) {
|
||||||
if (!newTagsForContentItem.Contains(tagContentItem.TagId)) {
|
if (!newTagsForContentItem.Contains(tagContentItem.TagId)) {
|
||||||
_authorizationService.CheckAccess(CurrentUser, Permissions.ApplyTag);
|
_authorizationService.CheckAccess(Permissions.ApplyTag, CurrentUser, null);
|
||||||
|
|
||||||
_tagsContentItemsRepository.Delete(tagContentItem);
|
_tagsContentItemsRepository.Delete(tagContentItem);
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@ namespace Orchard.Tags.Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach (var newTagForContentItem in newTagsForContentItem) {
|
foreach (var newTagForContentItem in newTagsForContentItem) {
|
||||||
_authorizationService.CheckAccess(CurrentUser, Permissions.ApplyTag);
|
_authorizationService.CheckAccess(Permissions.ApplyTag, CurrentUser, null);
|
||||||
|
|
||||||
_tagsContentItemsRepository.Create(new TagsContentItems { ContentItemId = contentItemId, TagId = newTagForContentItem });
|
_tagsContentItemsRepository.Create(new TagsContentItems { ContentItemId = contentItemId, TagId = newTagForContentItem });
|
||||||
}
|
}
|
||||||
|
@@ -26,7 +26,7 @@ namespace Orchard.Users.Controllers {
|
|||||||
|
|
||||||
|
|
||||||
public ActionResult Index() {
|
public ActionResult Index() {
|
||||||
if (!Services.Authorizer.Authorize(Permissions.ManageUsers, T("Not authorized to manage settings")))
|
if (!Services.Authorizer.Authorize(Permissions.ManageUsers, T("Not authorized to list users")))
|
||||||
return new HttpUnauthorizedResult();
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
var users = Services.ContentManager
|
var users = Services.ContentManager
|
||||||
@@ -44,7 +44,7 @@ namespace Orchard.Users.Controllers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ActionResult Create() {
|
public ActionResult Create() {
|
||||||
if (!Services.Authorizer.Authorize(Permissions.ManageUsers, T("Not authorized to manage settings")))
|
if (!Services.Authorizer.Authorize(Permissions.ManageUsers, T("Not authorized to manage users")))
|
||||||
return new HttpUnauthorizedResult();
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
var user = Services.ContentManager.New<IUser>(UserDriver.ContentType.Name);
|
var user = Services.ContentManager.New<IUser>(UserDriver.ContentType.Name);
|
||||||
@@ -56,7 +56,7 @@ namespace Orchard.Users.Controllers {
|
|||||||
|
|
||||||
[HttpPost, ActionName("Create")]
|
[HttpPost, ActionName("Create")]
|
||||||
public ActionResult CreatePOST() {
|
public ActionResult CreatePOST() {
|
||||||
if (!Services.Authorizer.Authorize(Permissions.ManageUsers, T("Not authorized to manage settings")))
|
if (!Services.Authorizer.Authorize(Permissions.ManageUsers, T("Not authorized to manage users")))
|
||||||
return new HttpUnauthorizedResult();
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
var model = new UserCreateViewModel();
|
var model = new UserCreateViewModel();
|
||||||
@@ -83,7 +83,7 @@ namespace Orchard.Users.Controllers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ActionResult Edit(int id) {
|
public ActionResult Edit(int id) {
|
||||||
if (!Services.Authorizer.Authorize(Permissions.ManageUsers, T("Not authorized to manage settings")))
|
if (!Services.Authorizer.Authorize(Permissions.ManageUsers, T("Not authorized to manage users")))
|
||||||
return new HttpUnauthorizedResult();
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
return View(new UserEditViewModel {
|
return View(new UserEditViewModel {
|
||||||
@@ -93,7 +93,7 @@ namespace Orchard.Users.Controllers {
|
|||||||
|
|
||||||
[HttpPost, ActionName("Edit")]
|
[HttpPost, ActionName("Edit")]
|
||||||
public ActionResult EditPOST(int id) {
|
public ActionResult EditPOST(int id) {
|
||||||
if (!Services.Authorizer.Authorize(Permissions.ManageUsers, T("Not authorized to manage settings")))
|
if (!Services.Authorizer.Authorize(Permissions.ManageUsers, T("Not authorized to manage users")))
|
||||||
return new HttpUnauthorizedResult();
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
var model = new UserEditViewModel {
|
var model = new UserEditViewModel {
|
||||||
|
@@ -137,6 +137,7 @@
|
|||||||
<Compile Include="Extensions\Loaders\AreaExtensionLoader.cs" />
|
<Compile Include="Extensions\Loaders\AreaExtensionLoader.cs" />
|
||||||
<Compile Include="Extensions\UriExtensions.cs" />
|
<Compile Include="Extensions\UriExtensions.cs" />
|
||||||
<Compile Include="OrchardException.cs" />
|
<Compile Include="OrchardException.cs" />
|
||||||
|
<Compile Include="Security\IAuthorizationServiceEvents.cs" />
|
||||||
<Compile Include="Security\StandardPermissions.cs" />
|
<Compile Include="Security\StandardPermissions.cs" />
|
||||||
<Compile Include="Security\OrchardSecurityException.cs" />
|
<Compile Include="Security\OrchardSecurityException.cs" />
|
||||||
<Compile Include="Tasks\Scheduling\IScheduledTask.cs" />
|
<Compile Include="Tasks\Scheduling\IScheduledTask.cs" />
|
||||||
|
@@ -1,4 +1,7 @@
|
|||||||
using JetBrains.Annotations;
|
using System;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Orchard.ContentManagement;
|
||||||
|
using Orchard.ContentManagement.Aspects;
|
||||||
using Orchard.Localization;
|
using Orchard.Localization;
|
||||||
using Orchard.Security.Permissions;
|
using Orchard.Security.Permissions;
|
||||||
using Orchard.UI.Notify;
|
using Orchard.UI.Notify;
|
||||||
@@ -6,6 +9,7 @@ using Orchard.UI.Notify;
|
|||||||
namespace Orchard.Security {
|
namespace Orchard.Security {
|
||||||
public interface IAuthorizer : IDependency {
|
public interface IAuthorizer : IDependency {
|
||||||
bool Authorize(Permission permission, LocalizedString message);
|
bool Authorize(Permission permission, LocalizedString message);
|
||||||
|
bool Authorize(Permission permission, IContent content, LocalizedString message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Authorizer : IAuthorizer {
|
public class Authorizer : IAuthorizer {
|
||||||
@@ -24,7 +28,11 @@ namespace Orchard.Security {
|
|||||||
public Localizer T { get; set; }
|
public Localizer T { get; set; }
|
||||||
|
|
||||||
public bool Authorize(Permission permission, LocalizedString message) {
|
public bool Authorize(Permission permission, LocalizedString message) {
|
||||||
if (_authorizationService.TryCheckAccess(CurrentUser, permission))
|
return Authorize(permission, null, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Authorize(Permission permission, IContent content, LocalizedString message) {
|
||||||
|
if (_authorizationService.TryCheckAccess(permission, CurrentUser, content))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (CurrentUser == null) {
|
if (CurrentUser == null) {
|
||||||
@@ -37,5 +45,6 @@ namespace Orchard.Security {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Orchard.Security.Permissions;
|
using Orchard.ContentManagement;
|
||||||
|
using Orchard.Security.Permissions;
|
||||||
|
|
||||||
namespace Orchard.Security {
|
namespace Orchard.Security {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -6,7 +7,9 @@ namespace Orchard.Security {
|
|||||||
/// provided by default.
|
/// provided by default.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IAuthorizationService : IDependency {
|
public interface IAuthorizationService : IDependency {
|
||||||
void CheckAccess(IUser user, Permission permission);
|
void CheckAccess(Permission permission, IUser user, IContent content);
|
||||||
bool TryCheckAccess(IUser user, Permission permission);
|
bool TryCheckAccess(Permission permission, IUser user, IContent content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
50
src/Orchard/Security/IAuthorizationServiceEvents.cs
Normal file
50
src/Orchard/Security/IAuthorizationServiceEvents.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
using Orchard.ContentManagement;
|
||||||
|
using Orchard.Security.Permissions;
|
||||||
|
|
||||||
|
namespace Orchard.Security {
|
||||||
|
public interface IAuthorizationServiceEvents : IEvents {
|
||||||
|
void Checking(CheckAccessContext context);
|
||||||
|
void Adjust(CheckAccessContext context);
|
||||||
|
void Complete(CheckAccessContext context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CheckAccessContext {
|
||||||
|
public Permission Permission { get; set; }
|
||||||
|
public IUser User { get; set; }
|
||||||
|
public IContent Content { get; set; }
|
||||||
|
public bool Granted { get; set; }
|
||||||
|
public bool Adjusted { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class AuthorizationServiceEvents : IAuthorizationServiceEvents {
|
||||||
|
public virtual void Checking(CheckAccessContext context) { }
|
||||||
|
public virtual void Adjust(CheckAccessContext context) { }
|
||||||
|
public virtual void Complete(CheckAccessContext context) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
using System;
|
||||||
|
using Orchard.ContentManagement;
|
||||||
|
using Orchard.Security.Permissions;
|
||||||
|
|
||||||
|
namespace Orchard.Security {
|
||||||
|
public interface IAuthorizationServiceEvents : IEvents {
|
||||||
|
void Checking(CheckAccessContext context);
|
||||||
|
void Adjust(CheckAccessContext context);
|
||||||
|
void Complete(CheckAccessContext context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CheckAccessContext {
|
||||||
|
public Permission Permission { get; set; }
|
||||||
|
public IUser User { get; set; }
|
||||||
|
public IContent Content { get; set; }
|
||||||
|
public bool Granted { get; set; }
|
||||||
|
public bool Adjusted { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class AuthorizationServiceEvents : IAuthorizationServiceEvents {
|
||||||
|
public virtual void Checking(CheckAccessContext context) { }
|
||||||
|
public virtual void Adjust(CheckAccessContext context) { }
|
||||||
|
public virtual void Complete(CheckAccessContext context) { }
|
||||||
|
}
|
||||||
|
}
|
@@ -5,7 +5,9 @@ namespace Orchard.Security.Permissions {
|
|||||||
public class Permission {
|
public class Permission {
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
|
||||||
public IEnumerable<Permission> ImpliedBy { get; set; }
|
public IEnumerable<Permission> ImpliedBy { get; set; }
|
||||||
|
public bool RequiresOwnership { get; set; }
|
||||||
|
|
||||||
public static Permission Named(string name) {
|
public static Permission Named(string name) {
|
||||||
return new Permission { Name = name };
|
return new Permission { Name = name };
|
||||||
|
@@ -34,11 +34,11 @@ namespace Orchard.UI.Navigation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<MenuItem> Reduce(IEnumerable<MenuItem> items) {
|
private IEnumerable<MenuItem> Reduce(IEnumerable<MenuItem> items) {
|
||||||
var hasDebugShowAllMenuItems = _authorizationService.TryCheckAccess(CurrentUser, Permission.Named("DebugShowAllMenuItems"));
|
var hasDebugShowAllMenuItems = _authorizationService.TryCheckAccess(Permission.Named("DebugShowAllMenuItems"), CurrentUser, null);
|
||||||
foreach (var item in items) {
|
foreach (var item in items) {
|
||||||
if (hasDebugShowAllMenuItems ||
|
if (hasDebugShowAllMenuItems ||
|
||||||
!item.Permissions.Any() ||
|
!item.Permissions.Any() ||
|
||||||
item.Permissions.Any(x => _authorizationService.TryCheckAccess(CurrentUser, x))) {
|
item.Permissions.Any(x => _authorizationService.TryCheckAccess(x, CurrentUser, null))) {
|
||||||
yield return new MenuItem {
|
yield return new MenuItem {
|
||||||
Items = Reduce(item.Items),
|
Items = Reduce(item.Items),
|
||||||
Permissions = item.Permissions,
|
Permissions = item.Permissions,
|
||||||
|
Reference in New Issue
Block a user