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:
loudej
2010-01-22 22:11:10 +00:00
parent 09c88f124c
commit e9b32d59ca
18 changed files with 260 additions and 80 deletions

View File

@@ -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;
} }
} }

View File

@@ -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;
} }

View File

@@ -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");
} }

View File

@@ -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");
} }

View File

@@ -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">

View File

@@ -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;
}
}
}

View File

@@ -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();

View File

@@ -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 {

View File

@@ -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);
for (var adjustmentLimiter = 0; adjustmentLimiter != 3; ++adjustmentLimiter) {
if (!context.Granted && context.User != null) {
if (String.Equals(context.User.UserName, "Administrator", StringComparison.OrdinalIgnoreCase) ||
((!String.IsNullOrEmpty(CurrentSite.SuperUser) && ((!String.IsNullOrEmpty(CurrentSite.SuperUser) &&
String.Equals(user.UserName, CurrentSite.SuperUser, StringComparison.OrdinalIgnoreCase)))) { String.Equals(context.User.UserName, CurrentSite.SuperUser, StringComparison.OrdinalIgnoreCase)))) {
return true; context.Granted = true;
}
} }
var grantingNames = PermissionNames(permission, Enumerable.Empty<string>()).ToArray(); 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;
IEnumerable<string> rolesForUser = user.As<IUserRoles>().Roles;
foreach (var role in rolesForUser) { foreach (var role in rolesForUser) {
RoleRecord roleRecord = _roleService.GetRoleByName(role); RoleRecord roleRecord = _roleService.GetRoleByName(role);
foreach (var permissionName in _roleService.GetPermissionsForRole(roleRecord.Id)) { foreach (var permissionName in _roleService.GetPermissionsForRole(roleRecord.Id)) {
string possessedName = permissionName; string possessedName = permissionName;
if (grantingNames.Any(grantingName => String.Equals(possessedName, grantingName, StringComparison.OrdinalIgnoreCase))) { if (grantingNames.Any(grantingName => String.Equals(possessedName, grantingName, StringComparison.OrdinalIgnoreCase))) {
return true; context.Granted = true;
} }
if (context.Granted)
break;
}
if (context.Granted)
break;
} }
} }
return false; context.Adjusted = false;
_events.Invoke(x => x.Adjust(context), Logger);
}
_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) {

View File

@@ -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();

View File

@@ -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 });
} }

View File

@@ -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 {

View File

@@ -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" />

View File

@@ -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;
} }
} }
} }

View File

@@ -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);
} }
} }

View 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) { }
}
}

View File

@@ -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 };

View File

@@ -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,