mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-27 04:19:04 +08:00
* Added permissions allowing management of users belonging to specific roles only * Added condition to manage superuser * Fixed the case where an user with no special ManageUserByRole Permission would end up being able to manage a user with no role, because of the behavior of Enumerable.All on empty collections.
This commit is contained in:
committed by
GitHub
parent
ffb56e684c
commit
aed260544d
@@ -137,6 +137,8 @@
|
||||
<Compile Include="Permissions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Conditions\RoleCondition.cs" />
|
||||
<Compile Include="Security\ManageUserByRolePermissions.cs" />
|
||||
<Compile Include="Security\ManageUserByRoleSecurityEventHandler.cs" />
|
||||
<Compile Include="Services\AssignRoleUserManagementActionsProvider.cs" />
|
||||
<Compile Include="Services\IRoleService.cs" />
|
||||
<Compile Include="Services\RolesBasedAuthorizationService.cs" />
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Data;
|
||||
using Orchard.Environment.Extensions.Models;
|
||||
using Orchard.Roles.Constants;
|
||||
using Orchard.Roles.Models;
|
||||
using Orchard.Security.Permissions;
|
||||
using UserPermissions = Orchard.Users.Permissions;
|
||||
|
||||
namespace Orchard.Roles.Security {
|
||||
public class ManageUserByRolePermissions : IPermissionProvider {
|
||||
private readonly IRepository<RoleRecord> _roleRepository;
|
||||
|
||||
public virtual Feature Feature { get; set; }
|
||||
|
||||
private static readonly Permission ManageUsersInRoleTemplate =
|
||||
new Permission {
|
||||
Description = "Manage Users in Role - {0}",
|
||||
Name = "ManageUsersInRole_{0}",
|
||||
ImpliedBy = new[] { UserPermissions.ManageUsers }
|
||||
};
|
||||
|
||||
public ManageUserByRolePermissions(
|
||||
// A dependency on IRoleService to get the list of roles would lead to a
|
||||
// circular dependency, because that service has methods to handle the
|
||||
// permissions for each specific role.
|
||||
IRepository<RoleRecord> roleRepository) {
|
||||
|
||||
_roleRepository = roleRepository;
|
||||
}
|
||||
|
||||
public static Permission CreatePermissionForManageUsersInRole(string roleName) {
|
||||
return new Permission {
|
||||
Description = string.Format(ManageUsersInRoleTemplate.Description, roleName),
|
||||
Name = string.Format(ManageUsersInRoleTemplate.Name, roleName),
|
||||
ImpliedBy = ManageUsersInRoleTemplate.ImpliedBy
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private IEnumerable<Permission> GetManageUsersInRolePermissions() {
|
||||
var allRoleNames = _roleRepository.Table
|
||||
.Select(r => r.Name)
|
||||
.ToList()
|
||||
// Never have to manage explicitly Anonymous or Authenticated roles
|
||||
.Except(SystemRoles.GetSystemRoles());
|
||||
foreach (var roleName in allRoleNames) {
|
||||
yield return CreatePermissionForManageUsersInRole(roleName);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Permission> GetPermissions() {
|
||||
foreach (var permission in GetManageUsersInRolePermissions()) {
|
||||
yield return permission;
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
|
||||
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {
|
||||
return Enumerable.Empty<PermissionStereotype>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Roles.Constants;
|
||||
using Orchard.Roles.Models;
|
||||
using Orchard.Roles.Services;
|
||||
using Orchard.Security;
|
||||
using UserPermissions = Orchard.Users.Permissions;
|
||||
|
||||
namespace Orchard.Roles.Security {
|
||||
public class ManageUserByRoleSecurityEventHandler : IAuthorizationServiceEventHandler {
|
||||
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
private readonly IRoleService _roleService;
|
||||
|
||||
private Lazy<IAuthorizationService> _authorizationService;
|
||||
|
||||
private string _superUserName;
|
||||
public ManageUserByRoleSecurityEventHandler(
|
||||
IWorkContextAccessor workContextAccessor,
|
||||
IRoleService roleService) {
|
||||
|
||||
_workContextAccessor = workContextAccessor;
|
||||
_roleService = roleService;
|
||||
|
||||
_superUserName = _workContextAccessor.GetContext().CurrentSite.SuperUser;
|
||||
_authorizationService = new Lazy<IAuthorizationService>(() =>
|
||||
_workContextAccessor.GetContext().Resolve<IAuthorizationService>());
|
||||
_allRoleNames = _roleService
|
||||
.GetRoles()
|
||||
.Select(r => r.Name)
|
||||
.Except(SystemRoles.GetSystemRoles());
|
||||
}
|
||||
|
||||
// memorize this to avoid fetching this information potentially several times per request
|
||||
private IEnumerable<string> _allRoleNames;
|
||||
|
||||
public void Adjust(CheckAccessContext context) {
|
||||
if (!context.Granted
|
||||
&& context.Permission == UserPermissions.ManageUsers) {
|
||||
// check that the user that is being managed is in the roles that
|
||||
// the current user is allowed to manage.
|
||||
var manager = context.User;
|
||||
var managed = context.Content.As<IUser>();
|
||||
if (manager != null) {
|
||||
|
||||
if (managed == null) {
|
||||
// Not checking permission to manage a specific user
|
||||
// Any "manage" permission is probably fine?
|
||||
var rolesToCheck = _allRoleNames;
|
||||
if (GrantPermission(rolesToCheck, manager, null)) {
|
||||
context.Granted = true;
|
||||
context.Adjusted = true;
|
||||
}
|
||||
} else {
|
||||
// We prevent Manage permissions on specific roles to affect the SuperUser. Only users
|
||||
// that actually have the full ManageUsers permissions will be able to manage them.
|
||||
if(!IsSuperUser(managed)) {
|
||||
// Checking permission to manage a specific user
|
||||
// The user we are attempting to manage must belong to to a subset of
|
||||
// all those roles.
|
||||
var theirRoleNames = managed
|
||||
.GetRuntimeUserRoles()
|
||||
// Never have to manage explicitly Anonymous or Authenticated roles
|
||||
.Except(SystemRoles.GetSystemRoles());
|
||||
|
||||
if (GrantPermission(theirRoleNames, manager, managed)) {
|
||||
context.Granted = true;
|
||||
context.Adjusted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool GrantPermission(
|
||||
IEnumerable<string> roleNamesToCheck,
|
||||
IUser manager, IUser managed) {
|
||||
|
||||
if (_authorizationService.Value != null) {
|
||||
if (managed == null) {
|
||||
// not checking on a specific user, so permission on any role is fine
|
||||
return roleNamesToCheck.Any(rn =>
|
||||
_authorizationService.Value.TryCheckAccess(
|
||||
ManageUserByRolePermissions.CreatePermissionForManageUsersInRole(rn),
|
||||
manager, managed));
|
||||
} else {
|
||||
// checking permissions on a specific user, so we need to have permissions
|
||||
// to manage all their roles
|
||||
if (roleNamesToCheck.Any()) {
|
||||
return roleNamesToCheck.All(rn =>
|
||||
_authorizationService.Value.TryCheckAccess(
|
||||
ManageUserByRolePermissions.CreatePermissionForManageUsersInRole(rn),
|
||||
manager, managed));
|
||||
} else {
|
||||
// if the specific user has no assigned role, they are just an "Authenticated" user.
|
||||
// Enumerable.All applied to that would return true, which may not be correct. We
|
||||
// only wish to return true if the user has any of the ManageUserByRole Permission.
|
||||
// To verify that, we test across all possible roles.
|
||||
return _allRoleNames
|
||||
.Any(rn =>
|
||||
_authorizationService.Value.TryCheckAccess(
|
||||
ManageUserByRolePermissions.CreatePermissionForManageUsersInRole(rn),
|
||||
manager, managed));
|
||||
}
|
||||
}
|
||||
}
|
||||
// if we can't test, fail the test
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsSuperUser(IUser user) {
|
||||
|
||||
var isSuperUser = string.Equals(user.UserName, _superUserName);
|
||||
// We could be testing the SiteOwner permission as well but:
|
||||
// - user can only have that permission if they belong to a Role the Permission is assigned to.
|
||||
// - if we can manage that role, then there's no reason why we should prevent it from being
|
||||
// managed here.
|
||||
return isSuperUser;
|
||||
}
|
||||
|
||||
public void Checking(CheckAccessContext context) { }
|
||||
|
||||
public void Complete(CheckAccessContext context) { }
|
||||
}
|
||||
}
|
||||
@@ -231,6 +231,7 @@ namespace Orchard.Users.Controllers {
|
||||
}
|
||||
|
||||
public ActionResult Edit(int id) {
|
||||
// check manage permission on any user
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageUsers, T("Not authorized to manage users")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
@@ -239,6 +240,11 @@ namespace Orchard.Users.Controllers {
|
||||
if (user == null)
|
||||
return HttpNotFound();
|
||||
|
||||
// check manage permission on specific user
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageUsers,
|
||||
user, T("Not authorized to manage users")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var editor = Shape.EditorTemplate(TemplateName: "Parts/User.Edit", Model: new UserEditViewModel {User = user}, Prefix: null);
|
||||
editor.Metadata.Position = "2";
|
||||
var model = Services.ContentManager.BuildEditor(user);
|
||||
@@ -257,6 +263,11 @@ namespace Orchard.Users.Controllers {
|
||||
if (user == null)
|
||||
return HttpNotFound();
|
||||
|
||||
// check manage permission on specific user
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageUsers,
|
||||
user, T("Not authorized to manage users")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
string previousName = user.UserName;
|
||||
|
||||
var model = Services.ContentManager.UpdateEditor(user, this);
|
||||
@@ -306,6 +317,11 @@ namespace Orchard.Users.Controllers {
|
||||
if (user == null)
|
||||
return HttpNotFound();
|
||||
|
||||
// check manage permission on specific user
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageUsers,
|
||||
user, T("Not authorized to manage users")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
if (string.Equals(Services.WorkContext.CurrentSite.SuperUser, user.UserName, StringComparison.Ordinal)) {
|
||||
Services.Notifier.Error(T("The Super user can't be removed. Please disable this account or specify another Super user account."));
|
||||
}
|
||||
@@ -330,6 +346,11 @@ namespace Orchard.Users.Controllers {
|
||||
if (user == null)
|
||||
return HttpNotFound();
|
||||
|
||||
// check manage permission on specific user
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageUsers,
|
||||
user, T("Not authorized to manage users")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var siteUrl = Services.WorkContext.CurrentSite.BaseUrl;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(siteUrl)) {
|
||||
@@ -352,6 +373,11 @@ namespace Orchard.Users.Controllers {
|
||||
if (user == null)
|
||||
return HttpNotFound();
|
||||
|
||||
// check manage permission on specific user
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageUsers,
|
||||
user, T("Not authorized to manage users")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
user.As<UserPart>().RegistrationStatus = UserStatus.Approved;
|
||||
Services.Notifier.Success(T("User {0} approved", user.UserName));
|
||||
_userEventHandlers.Approved(user);
|
||||
@@ -369,6 +395,11 @@ namespace Orchard.Users.Controllers {
|
||||
if (user == null)
|
||||
return HttpNotFound();
|
||||
|
||||
// check manage permission on specific user
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageUsers,
|
||||
user, T("Not authorized to manage users")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
if (string.Equals(Services.WorkContext.CurrentUser.UserName, user.UserName, StringComparison.Ordinal)) {
|
||||
Services.Notifier.Error(T("You can't disable your own account. Please log in with another account"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user