Roles management (#8535)

* Started adding roles that will be used to to allow users the ability to assign
specific roles to others.

* small refactor of the method to return dynamic AssignRole permissions

* Implemented permissions to limit the ability of a user to assign specific
roles

* controller action to assign roles

* Refactored Assign action in roles admin Controller
Fixed a bug in figuring out the id of roles to assign
Added provider to add action links to list of users in BO
Added "cancel" button to page to assign roles, based on returnUrl
This commit is contained in:
Matteo Piovanelli
2022-02-09 10:23:34 +01:00
committed by GitHub
parent 0d410d5ec6
commit f2a8450d90
16 changed files with 464 additions and 64 deletions

View File

@@ -2,10 +2,14 @@
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Orchard.ContentManagement;
using Orchard.Data;
using Orchard.DisplayManagement;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Mvc;
using Orchard.Mvc.Extensions;
using Orchard.Roles.Events;
using Orchard.Roles.Models;
using Orchard.Roles.Services;
using Orchard.Roles.ViewModels;
@@ -17,20 +21,38 @@ namespace Orchard.Roles.Controllers {
public class AdminController : Controller {
private readonly IRoleService _roleService;
private readonly IAuthorizationService _authorizationService;
private readonly IWorkContextAccessor _workContextAccessor;
private readonly IContentManager _contentManager;
private readonly IRepository<UserRolesPartRecord> _userRolesRepository;
private readonly INotifier _notifier;
private readonly IRoleEventHandler _roleEventHandlers;
public AdminController(
IOrchardServices services,
IRoleService roleService,
INotifier notifier,
IAuthorizationService authorizationService) {
IAuthorizationService authorizationService,
IWorkContextAccessor workContextAccessor,
IContentManager contentManager,
IShapeFactory shapeFactory,
IRepository<UserRolesPartRecord> userRolesRepository,
IRoleEventHandler roleEventHandlers) {
Services = services;
_roleService = roleService;
_authorizationService = authorizationService;
_workContextAccessor = workContextAccessor;
_contentManager = contentManager;
_userRolesRepository = userRolesRepository;
_notifier = notifier;
_roleEventHandlers = roleEventHandlers;
T = NullLocalizer.Instance;
Logger = NullLogger.Instance;
Shape = shapeFactory;
}
dynamic Shape { get; set; }
public IOrchardServices Services { get; set; }
public Localizer T { get; set; }
public ILogger Logger { get; set; }
@@ -175,5 +197,118 @@ namespace Orchard.Roles.Controllers {
return this.RedirectLocal(returnUrl, () => RedirectToAction("Index"));
}
[Authorize]
public ActionResult Assign(int id) {
// CurrentUser is trying to access a page to assign roles to the User
// with Id id.
var currentUser = _workContextAccessor.GetContext().CurrentUser;
// Get the user whose roles we want to assign
var userRolesPart = _contentManager.Get<UserRolesPart>(id);
if (userRolesPart == null) {
return HttpNotFound();
}
// Check whether the current user has any of the required permissions
var allRoles = _roleService.GetRoles();
var authorizedRoleIds = allRoles
.Where(rr => _authorizationService.TryCheckAccess(
Permissions.CreatePermissionForAssignRole(rr.Name),
currentUser,
userRolesPart))
.Select(rr => rr.Id).ToList();
if (!authorizedRoleIds.Any()) {
return new HttpUnauthorizedResult();
}
// create the ViewModel used to manage a user's roles
var model = new UserRolesViewModel {
User = userRolesPart.As<IUser>(),
UserRoles = userRolesPart,
Roles = allRoles.Select(x => new UserRoleEntry {
RoleId = x.Id,
Name = x.Name,
Granted = userRolesPart.Roles.Contains(x.Name)
}).ToList(),
AuthorizedRoleIds = authorizedRoleIds
};
// this calls the same view used by the driver that lets users with higher
// permissions do the same.
return AssignView(model);
}
[HttpPost, ActionName("Assign"), Authorize]
public ActionResult AssignPOST(int id, string returnUrl) {
var currentUser = _workContextAccessor.GetContext().CurrentUser;
// Get the user whose roles we want to assign
var userRolesPart = _contentManager.Get<UserRolesPart>(id);
if (userRolesPart == null) {
return HttpNotFound();
}
// Check whether the current user has any of the required permissions
var allRoles = _roleService.GetRoles();
var authorizedRoleIds = allRoles
.Where(rr => _authorizationService.TryCheckAccess(
Permissions.CreatePermissionForAssignRole(rr.Name),
currentUser,
userRolesPart))
.Select(rr => rr.Id).ToList();
if (!authorizedRoleIds.Any()) {
return new HttpUnauthorizedResult();
}
// Start trying to update
var editModel = new UserRolesViewModel {
User = userRolesPart.As<IUser>(),
UserRoles = userRolesPart
};
if (TryUpdateModel(editModel)) {
// same logic that is used in the UserRolesPartDriver:
var currentUserRoleRecords = _userRolesRepository.Fetch(x => x.UserId == editModel.User.Id).ToArray();
var currentRoleRecords = currentUserRoleRecords.Select(x => x.Role);
// The roles the user should have after the update (pending a verification that
// the currentUser is allowed to assign them)
var targetRoleRecords = editModel.Roles.Where(x => x.Granted).Select(x => _roleService.GetRole(x.RoleId)).ToArray();
foreach (var addingRole in targetRoleRecords
.Where(x =>
// user doesn't have the role yet
!currentRoleRecords.Contains(x)
// && we are authorized to assign this role
&& authorizedRoleIds.Contains(x.Id))) {
_notifier.Warning(T("Adding role {0} to user {1}", addingRole.Name, userRolesPart.As<IUser>().UserName));
_userRolesRepository.Create(new UserRolesPartRecord { UserId = editModel.User.Id, Role = addingRole });
_roleEventHandlers.UserAdded(new UserAddedContext { Role = addingRole, User = editModel.User });
}
foreach (var removingRole in currentUserRoleRecords
.Where(x =>
// user has this role that they shouldn't
!targetRoleRecords.Contains(x.Role)
// && we are authorized to assign this role
&& authorizedRoleIds.Contains(x.Role.Id))) {
_notifier.Warning(T("Removing role {0} from user {1}", removingRole.Role.Name, userRolesPart.As<IUser>().UserName));
_userRolesRepository.Delete(removingRole);
_roleEventHandlers.UserRemoved(new UserRemovedContext { Role = removingRole.Role, User = editModel.User });
}
}
if (!ModelState.IsValid) {
editModel.AuthorizedRoleIds = authorizedRoleIds;
// Something went wrong in the update
Services.TransactionManager.Cancel();
return AssignView(editModel);
}
return this.RedirectLocal(returnUrl, () => RedirectToAction("Assign", new { id = id }));
}
private ActionResult AssignView(UserRolesViewModel editModel) {
var editor = Shape.EditorTemplate(
TemplateName: "Parts/Roles.UserRoles",
Model: editModel,
Prefix: null);
return View(editor
.UserName(editModel.User?.UserName));
}
}
}

View File

@@ -10,6 +10,7 @@ using Orchard.Roles.Services;
using Orchard.Roles.ViewModels;
using Orchard.Security;
using Orchard.UI.Notify;
using System.Collections.Generic;
namespace Orchard.Roles.Drivers {
public class UserRolesPartDriver : ContentPartDriver<UserRolesPart> {
@@ -22,11 +23,11 @@ namespace Orchard.Roles.Drivers {
private const string TemplateName = "Parts/Roles.UserRoles";
public UserRolesPartDriver(
IRepository<UserRolesPartRecord> userRolesRepository,
IRoleService roleService,
IRepository<UserRolesPartRecord> userRolesRepository,
IRoleService roleService,
INotifier notifier,
IAuthenticationService authenticationService,
IAuthorizationService authorizationService,
IAuthorizationService authorizationService,
IRoleEventHandler roleEventHandlers) {
_userRolesRepository = userRolesRepository;
@@ -36,6 +37,8 @@ namespace Orchard.Roles.Drivers {
_authorizationService = authorizationService;
_roleEventHandlers = roleEventHandlers;
T = NullLocalizer.Instance;
_allRoles = new Lazy<IEnumerable<RoleRecord>>(() => _roleService.GetRoles());
}
protected override string Prefix {
@@ -46,49 +49,90 @@ namespace Orchard.Roles.Drivers {
public Localizer T { get; set; }
private Lazy<IEnumerable<RoleRecord>> _allRoles;
protected override DriverResult Editor(UserRolesPart userRolesPart, dynamic shapeHelper) {
// don't show editor without apply roles permission
if (!_authorizationService.TryCheckAccess(Permissions.AssignRoles, _authenticationService.GetAuthenticatedUser(), userRolesPart))
return null;
return ContentShape("Parts_Roles_UserRoles_Edit",
() => {
var roles =_roleService.GetRoles().Select(x => new UserRoleEntry {
RoleId = x.Id,
Name = x.Name,
Granted = userRolesPart.Roles.Contains(x.Name)});
var model = new UserRolesViewModel {
User = userRolesPart.As<IUser>(),
UserRoles = userRolesPart,
Roles = roles.ToList(),
};
return shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix);
});
() => {
var currentUser = _authenticationService.GetAuthenticatedUser();
// Get the roles we are authorized to assign
var authorizedRoleIds = _allRoles.Value
.Where(rr => _authorizationService.TryCheckAccess(
Permissions.CreatePermissionForAssignRole(rr.Name),
currentUser,
userRolesPart))
.Select(rr => rr.Id).ToList();
// If the user has no roles they can assign, we will show nothing
if (!authorizedRoleIds.Any()) {
return null;
}
var allRoles = _allRoles.Value
.Select(x => new UserRoleEntry {
RoleId = x.Id,
Name = x.Name,
Granted = userRolesPart.Roles.Contains(x.Name)
});
var model = new UserRolesViewModel {
User = userRolesPart.As<IUser>(),
UserRoles = userRolesPart,
Roles = allRoles.ToList(),
AuthorizedRoleIds = authorizedRoleIds
};
return shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix);
});
}
protected override DriverResult Editor(UserRolesPart userRolesPart, IUpdateModel updater, dynamic shapeHelper) {
// don't apply editor without apply roles permission
if (!_authorizationService.TryCheckAccess(Permissions.AssignRoles, _authenticationService.GetAuthenticatedUser(), userRolesPart))
return null;
var currentUser = _authenticationService.GetAuthenticatedUser();
// Get the roles we are authorized to assign
var authorizedRoleIds = _allRoles.Value
.Where(rr => _authorizationService.TryCheckAccess(
Permissions.CreatePermissionForAssignRole(rr.Name),
currentUser,
userRolesPart))
.Select(rr => rr.Id).ToList();
var model = BuildEditorViewModel(userRolesPart);
if (updater.TryUpdateModel(model, Prefix, null, null)) {
var currentUserRoleRecords = _userRolesRepository.Fetch(x => x.UserId == model.User.Id).ToArray();
var currentRoleRecords = currentUserRoleRecords.Select(x => x.Role);
var targetRoleRecords = model.Roles.Where(x => x.Granted).Select(x => _roleService.GetRole(x.RoleId)).ToArray();
foreach (var addingRole in targetRoleRecords.Where(x => !currentRoleRecords.Contains(x))) {
_notifier.Warning(T("Adding role {0} to user {1}", addingRole.Name, userRolesPart.As<IUser>().UserName));
_userRolesRepository.Create(new UserRolesPartRecord { UserId = model.User.Id, Role = addingRole });
_roleEventHandlers.UserAdded(new UserAddedContext {Role = addingRole, User = model.User});
}
foreach (var removingRole in currentUserRoleRecords.Where(x => !targetRoleRecords.Contains(x.Role))) {
_notifier.Warning(T("Removing role {0} from user {1}", removingRole.Role.Name, userRolesPart.As<IUser>().UserName));
_userRolesRepository.Delete(removingRole);
_roleEventHandlers.UserRemoved(new UserRemovedContext { Role = removingRole.Role, User = model.User });
// We only have something to do for the roles the user is allowed to assign. We do this check
// after the TryUpdateModel so that even if we do nothing, we'll display things as the user
// changed them.
if (authorizedRoleIds.Any()) {
// Find all RoleRecord objects for the user: these are roles that are already
// assigned to them.
var currentUserRoleRecords = _userRolesRepository.Fetch(x => x.UserId == model.User.Id).ToArray();
var currentRoleRecords = currentUserRoleRecords.Select(x => x.Role);
// The roles the user should have after the update (pending a verification that
// the currentUser is allowed to assign them)
var targetRoleRecords = model.Roles.Where(x => x.Granted).Select(x => _roleService.GetRole(x.RoleId)).ToArray();
foreach (var addingRole in targetRoleRecords
.Where(x =>
// user doesn't have the role yet
!currentRoleRecords.Contains(x)
// && we are authorized to assign this role
&& authorizedRoleIds.Contains(x.Id))) {
_notifier.Warning(T("Adding role {0} to user {1}", addingRole.Name, userRolesPart.As<IUser>().UserName));
_userRolesRepository.Create(new UserRolesPartRecord { UserId = model.User.Id, Role = addingRole });
_roleEventHandlers.UserAdded(new UserAddedContext { Role = addingRole, User = model.User });
}
foreach (var removingRole in currentUserRoleRecords
.Where(x =>
// user has this role that they shouldn't
!targetRoleRecords.Contains(x.Role)
// && we are authorized to assign this role
&& authorizedRoleIds.Contains(x.Role.Id))) {
_notifier.Warning(T("Removing role {0} from user {1}", removingRole.Role.Name, userRolesPart.As<IUser>().UserName));
_userRolesRepository.Delete(removingRole);
_roleEventHandlers.UserRemoved(new UserRemovedContext { Role = removingRole.Role, User = model.User });
}
}
}
model.AuthorizedRoleIds = authorizedRoleIds;
return ContentShape("Parts_Roles_UserRoles_Edit",
() => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix));
() => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix));
}
private static UserRolesViewModel BuildEditorViewModel(UserRolesPart userRolesPart) {

View File

@@ -137,6 +137,7 @@
<Compile Include="Permissions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Conditions\RoleCondition.cs" />
<Compile Include="Services\AssignRoleUserManagementActionsProvider.cs" />
<Compile Include="Services\IRoleService.cs" />
<Compile Include="Services\RolesBasedAuthorizationService.cs" />
<Compile Include="Services\RoleService.cs" />
@@ -216,6 +217,7 @@
<ItemGroup>
<None Include="packages.config" />
<Content Include="Views\EditorTemplates\Parts\Roles.UserSuspensionSettings.cshtml" />
<Content Include="Views\Admin\Assign.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>

View File

@@ -1,18 +1,63 @@
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.Roles.Services;
using Orchard.Security.Permissions;
namespace Orchard.Roles {
public class Permissions : IPermissionProvider {
private readonly IRepository<RoleRecord> _roleRepository;
public static readonly Permission ManageRoles = new Permission { Description = "Managing Roles", Name = "ManageRoles" };
public static readonly Permission AssignRoles = new Permission { Description = "Assign Roles", Name = "AssignRoles", ImpliedBy = new [] { ManageRoles } };
public static readonly Permission AssignRoles = new Permission { Description = "Assign Roles", Name = "AssignRoles", ImpliedBy = new[] { ManageRoles } };
public virtual Feature Feature { get; set; }
public IEnumerable<Permission> GetPermissions() {
return new[] {
ManageRoles, AssignRoles
private static readonly Permission AssignRoleTemplate =
new Permission {
Description = "Assign Role - {0}",
Name = "AssignRole_{0}",
ImpliedBy = new[] { ManageRoles, AssignRoles }
};
public Permissions(
// 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 CreatePermissionForAssignRole(string roleName) {
return new Permission {
Description = string.Format(AssignRoleTemplate.Description, roleName),
Name = string.Format(AssignRoleTemplate.Name, roleName),
ImpliedBy = AssignRoleTemplate.ImpliedBy
};
}
private IEnumerable<Permission> GetAssignRolePermissions() {
var allRoleNames = _roleRepository.Table
.Select(r => r.Name)
.ToList()
// Never have to assign Anonymous or Authenticated roles
.Except(SystemRoles.GetSystemRoles());
foreach (var roleName in allRoleNames) {
yield return CreatePermissionForAssignRole(roleName);
}
}
public IEnumerable<Permission> GetPermissions() {
yield return ManageRoles;
yield return AssignRoles;
foreach (var permission in GetAssignRolePermissions()) {
yield return permission;
}
yield break;
}
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {

View File

@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using Orchard.ContentManagement;
using Orchard.Localization;
using Orchard.Roles.Models;
using Orchard.Security;
using Orchard.Users.Services;
namespace Orchard.Roles.Services {
public class AssignRoleUserManagementActionsProvider : IUserManagementActionsProvider {
private readonly IRoleService _roleService;
private readonly IAuthorizationService _authorizationService;
private readonly IAuthenticationService _authenticationService;
private readonly IWorkContextAccessor _workContextAccessor;
private Lazy<IEnumerable<RoleRecord>> _allRoles;
public AssignRoleUserManagementActionsProvider(
IRoleService roleService,
IAuthorizationService authorizationService,
IAuthenticationService authenticationService,
IWorkContextAccessor workContextAccessor) {
_roleService = roleService;
_authorizationService = authorizationService;
_authenticationService = authenticationService;
_workContextAccessor = workContextAccessor;
T = NullLocalizer.Instance;
_allRoles = new Lazy<IEnumerable<RoleRecord>>(() => _roleService.GetRoles());
}
public Localizer T { get; set; }
public IEnumerable<Func<HtmlHelper, MvcHtmlString>> UserActionLinks(IUser user) {
// Get the user whose roles we want to assign
var userRolesPart = user.As<UserRolesPart>();
if (userRolesPart == null) {
yield break;
}
var currentUser = _authenticationService.GetAuthenticatedUser();
// Get the roles we are authorized to assign
var authorizedRoleIds = _allRoles.Value
.Where(rr => _authorizationService.TryCheckAccess(
Permissions.CreatePermissionForAssignRole(rr.Name),
currentUser,
userRolesPart))
.Select(rr => rr.Id).ToList();
// If the user has no roles they can assign, we will show nothing
if (!authorizedRoleIds.Any()) {
yield break;
}
yield return (Func<HtmlHelper, MvcHtmlString>)
(Html => Html.ActionLink(
T("Roles").ToString(),
"Assign",
new {
Area = "Orchard.Roles",
Controller = "Admin",
id = user.Id,
returnUrl = Html.ViewContext.RequestContext.HttpContext.Request.RawUrl
}));
}
}
}

View File

@@ -6,11 +6,13 @@ namespace Orchard.Roles.ViewModels {
public class UserRolesViewModel {
public UserRolesViewModel() {
Roles = new List<UserRoleEntry>();
AuthorizedRoleIds = new List<int>();
}
public IUser User { get; set; }
public IUserRoles UserRoles { get; set; }
public IList<UserRoleEntry> Roles { get; set; }
public IList<int> AuthorizedRoleIds { get; set; }
}
public class UserRoleEntry {

View File

@@ -0,0 +1,35 @@
@using Orchard.Utility.Extensions
@{
Layout.Title = T("Assign Roles").ToString();
var userName = (string)Model.UserName ?? string.Empty;
var returnUrl = Request.QueryString["returnUrl"];
}
@using (Html.BeginFormAntiForgeryPost()) {
@Html.ValidationSummary()
<div class="edit-item">
<div class="edit-item-primary">
<div class="edit-item-content">
<fieldset>
<legend>@T("Assign roles for user {0}", userName)</legend>
@* Model is a Shape, calling Display() so that it is rendered using the most specific template for its Shape type *@
@Display(Model)
</fieldset>
</div>
</div>
<div class="edit-item-secondary group">
<div class="edit-item-sidebar group">
<fieldset class="save-button">
<button class="primaryAction" type="submit">@T("Save")</button>
</fieldset>
@if (!String.IsNullOrWhiteSpace(returnUrl) && Request.IsLocalUrl(returnUrl)) {
<fieldset class="cancel-button">
<a id="button-cancel" href="@returnUrl" class="button">@T("Cancel")</a>
</fieldset>
}
</div>
</div>
</div>
}

View File

@@ -18,14 +18,17 @@
@Html.Hidden("Roles[" + index + "].Name", entry.Name)
<div>
@Html.CheckBox("Roles[" + index + "].Granted", entry.Granted)
@if (Model.AuthorizedRoleIds.Contains(entry.RoleId)) {
@Html.CheckBox("Roles[" + index + "].Granted", entry.Granted)
} else {
@Html.CheckBox("Roles[" + index + "].Granted", entry.Granted, new { disabled = true })
}
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.Roles[index].Granted)">@entry.Name</label>
</div>
index++;
}
}
else {
} else {
<p>@T("There are no roles.")</p>
}
</fieldset>

View File

@@ -12,7 +12,7 @@ namespace Orchard.Users {
.Add(T("Users"), "11",
menu => menu.Action("Index", "Admin", new { area = "Orchard.Users" })
.Add(T("Users"), "1.0", item => item.Action("Index", "Admin", new { area = "Orchard.Users" })
.LocalNav().Permission(Permissions.ManageUsers)));
.LocalNav().Permission(Permissions.ViewUsers)));
}
}
}

View File

@@ -27,6 +27,7 @@ namespace Orchard.Users.Controllers {
private readonly IUserService _userService;
private readonly IUserEventHandler _userEventHandlers;
private readonly ISiteService _siteService;
private readonly IEnumerable<IUserManagementActionsProvider> _userManagementActionsProviders;
public AdminController(
IOrchardServices services,
@@ -34,13 +35,15 @@ namespace Orchard.Users.Controllers {
IUserService userService,
IShapeFactory shapeFactory,
IUserEventHandler userEventHandlers,
ISiteService siteService) {
ISiteService siteService,
IEnumerable<IUserManagementActionsProvider> userManagementActionsProviders) {
Services = services;
_membershipService = membershipService;
_userService = userService;
_userEventHandlers = userEventHandlers;
_siteService = siteService;
_userManagementActionsProviders = userManagementActionsProviders;
T = NullLocalizer.Instance;
Shape = shapeFactory;
@@ -51,7 +54,7 @@ namespace Orchard.Users.Controllers {
public Localizer T { get; set; }
public ActionResult Index(UserIndexOptions options, PagerParameters pagerParameters) {
if (!Services.Authorizer.Authorize(Permissions.ManageUsers, T("Not authorized to list users")))
if (!Services.Authorizer.Authorize(Permissions.ViewUsers, T("Not authorized to list users")))
return new HttpUnauthorizedResult();
var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
@@ -102,7 +105,12 @@ namespace Orchard.Users.Controllers {
var model = new UsersIndexViewModel {
Users = results
.Select(x => new UserEntry { User = x.Record })
.Select(x => new UserEntry {
UserPart = x,
User = x.Record,
AdditionalActionLinks = _userManagementActionsProviders
.SelectMany(p => p.UserActionLinks(x)).ToList()
})
.ToList(),
Options = options,
Pager = pagerShape

View File

@@ -140,7 +140,9 @@
<Compile Include="Services\ApproveUserService.cs" />
<Compile Include="Services\AccountValidationService.cs" />
<Compile Include="Services\AuthenticationRedirectionFilter.cs" />
<Compile Include="Services\DefaultUserManagementActionsProvider.cs" />
<Compile Include="Services\InactiveUserSuspensionBackgroundTask.cs" />
<Compile Include="Services\IUserManagementActionsProvider.cs" />
<Compile Include="Services\IUserService.cs" />
<Compile Include="Services\IUserSuspensionConditionProvider.cs" />
<Compile Include="Services\MembershipValidationService.cs" />

View File

@@ -4,13 +4,16 @@ using Orchard.Security.Permissions;
namespace Orchard.Users {
public class Permissions : IPermissionProvider {
public static readonly Permission ManageUsers = new Permission { Description = "Managing Users", Name = "ManageUsers" };
public static readonly Permission ManageUsers =
new Permission { Description = "Managing Users", Name = "ManageUsers" };
public static readonly Permission ViewUsers =
new Permission { Description = "View List of Users", Name = "ViewUsers", ImpliedBy = new[] { ManageUsers } };
public virtual Feature Feature { get; set; }
public IEnumerable<Permission> GetPermissions() {
return new[] {
ManageUsers,
ManageUsers, ViewUsers
};
}
@@ -18,7 +21,7 @@ namespace Orchard.Users {
return new[] {
new PermissionStereotype {
Name = "Administrator",
Permissions = new[] {ManageUsers}
Permissions = new[] { ManageUsers, ViewUsers }
},
};
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Orchard.Security;
namespace Orchard.Users.Services {
public class DefaultUserManagementActionsProvider : IUserManagementActionsProvider {
public IEnumerable<Func<HtmlHelper, MvcHtmlString>> UserActionLinks(IUser user) {
yield break;
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;
using Orchard.Security;
namespace Orchard.Users.Services {
public interface IUserManagementActionsProvider : IDependency {
// Using a delegate so implementations don't have to build/figure out
// their own HtmlHelper
IEnumerable<Func<HtmlHelper, MvcHtmlString>> UserActionLinks(IUser user);
}
}

View File

@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Orchard.Users.Models;
namespace Orchard.Users.ViewModels {
@@ -10,8 +12,14 @@ namespace Orchard.Users.ViewModels {
}
public class UserEntry {
public UserEntry() {
AdditionalActionLinks = new List<Func<HtmlHelper, MvcHtmlString>>();
}
public UserPart UserPart { get; set; }
public UserPartRecord User { get; set; }
public bool IsChecked { get; set; }
public List<Func<HtmlHelper, MvcHtmlString>> AdditionalActionLinks { get; set; }
}
public class UserIndexOptions {

View File

@@ -1,4 +1,5 @@
@model Orchard.Users.ViewModels.UsersIndexViewModel
@using Orchard.Users;
@using Orchard.Users.Models;
@using Orchard.Users.ViewModels;
@{
@@ -53,6 +54,7 @@
</tr>
</thead>
@foreach (var entry in Model.Users) {
var canManageUser = Authorizer.Authorize(Permissions.ManageUsers, entry.UserPart);
<tr>
<td>
<input type="hidden" value="@Model.Users[userIndex].User.Id" name="@Html.NameOf(m => m.Users[userIndex].User.Id)" />
@@ -65,32 +67,43 @@
else {
<img class="icon" src="@Href("~/Modules/Orchard.Users/Content/Admin/images/offline.gif") " alt="@T("Moderated") " title="@if (entry.User.EmailStatus == UserStatus.Approved) { @T("User is moderated") } else { @T("E-mail validation is pending") }" />
}
@Html.ActionLink(entry.User.UserName, "Edit", new { entry.User.Id })
@if (canManageUser) {
@Html.ActionLink(entry.User.UserName, "Edit", new { entry.User.Id })
} else {
@entry.User.UserName
}
</td>
<td>
@entry.User.Email
</td>
<td>
<ul class="action-links">
<li class="action-link">
@Html.ActionLink(T("Edit").ToString(), "Edit", new { entry.User.Id })
</li>
<li class="action-link">
@Html.ActionLink(T("Delete").ToString(), "Delete", new { entry.User.Id }, new { itemprop = "RemoveUrl UnsafeUrl" })
</li>
@if (entry.User.RegistrationStatus == UserStatus.Pending) {
@if (canManageUser) {
@* TODO: what permission should we check for the "Edit" Action *@
<li class="action-link">
@Html.ActionLink(T("Approve").ToString(), "Approve", new { entry.User.Id }, new { itemprop = "UnsafeUrl" })
@Html.ActionLink(T("Edit").ToString(), "Edit", new { entry.User.Id })
</li>
}
else {
<li class="action-link">
@Html.ActionLink(T("Disable").ToString(), "Moderate", new { entry.User.Id }, new { itemprop = "UnsafeUrl" })
@Html.ActionLink(T("Delete").ToString(), "Delete", new { entry.User.Id }, new { itemprop = "RemoveUrl UnsafeUrl" })
</li>
if (entry.User.RegistrationStatus == UserStatus.Pending) {
<li class="action-link">
@Html.ActionLink(T("Approve").ToString(), "Approve", new { entry.User.Id }, new { itemprop = "UnsafeUrl" })
</li>
} else {
<li class="action-link">
@Html.ActionLink(T("Disable").ToString(), "Moderate", new { entry.User.Id }, new { itemprop = "UnsafeUrl" })
</li>
}
if (entry.User.EmailStatus == UserStatus.Pending) {
<li class="action-link">
@Html.ActionLink(T("Send challenge E-mail").ToString(), "SendChallengeEmail", new { entry.User.Id }, new { itemprop = "UnsafeUrl" })
</li>
}
}
@if (entry.User.EmailStatus == UserStatus.Pending) {
@foreach(var actionLink in entry.AdditionalActionLinks) {
<li class="action-link">
@Html.ActionLink(T("Send challenge E-mail").ToString(), "SendChallengeEmail", new { entry.User.Id }, new { itemprop = "UnsafeUrl" })
@actionLink(Html)
</li>
}
</ul>