Skip to content

Commit

Permalink
Enhance User management by introducing List, Edit, Assign, and Delete…
Browse files Browse the repository at this point in the history
… permissions (#12407)
  • Loading branch information
MikeAlhayek committed Jan 18, 2023
1 parent 07698e4 commit 009daa3
Show file tree
Hide file tree
Showing 44 changed files with 889 additions and 462 deletions.
10 changes: 5 additions & 5 deletions src/OrchardCore.Modules/OrchardCore.Users/AdminMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ public Task BuildNavigationAsync(string name, NavigationBuilder builder)
.Add(S["Users"], S["Users"].PrefixPosition(), users => users
.AddClass("users").Id("users")
.Action("Index", "Admin", "OrchardCore.Users")
.Permission(Permissions.ViewUsers)
.Permission(CommonPermissions.ListUsers)
.Resource(new User())
.LocalNav()
)
.Add(S["Settings"], settings => settings
.Add(S["User Login"], S["User Login"].PrefixPosition(), login => login
.Permission(Permissions.ManageUsers)
.Permission(CommonPermissions.ManageUsers)
.Action("Index", "Admin", new { area = "OrchardCore.Settings", groupId = LoginSettingsDisplayDriver.GroupId })
.LocalNav()
)
Expand Down Expand Up @@ -67,7 +67,7 @@ public Task BuildNavigationAsync(string name, NavigationBuilder builder)
.Add(S["Security"], security => security
.Add(S["Settings"], settings => settings
.Add(S["User Change email"], S["User Change email"].PrefixPosition(), registration => registration
.Permission(Permissions.ManageUsers)
.Permission(CommonPermissions.ManageUsers)
.Action("Index", "Admin", new { area = "OrchardCore.Settings", groupId = ChangeEmailSettingsDisplayDriver.GroupId })
.LocalNav()
)));
Expand Down Expand Up @@ -97,7 +97,7 @@ public Task BuildNavigationAsync(string name, NavigationBuilder builder)
.Add(S["Security"], security => security
.Add(S["Settings"], settings => settings
.Add(S["User Registration"], S["User Registration"].PrefixPosition(), registration => registration
.Permission(Permissions.ManageUsers)
.Permission(CommonPermissions.ManageUsers)
.Action("Index", "Admin", new { area = "OrchardCore.Settings", groupId = RegistrationSettingsDisplayDriver.GroupId })
.LocalNav()
)));
Expand Down Expand Up @@ -127,7 +127,7 @@ public Task BuildNavigationAsync(string name, NavigationBuilder builder)
.Add(S["Security"], security => security
.Add(S["Settings"], settings => settings
.Add(S["User Reset password"], S["User Reset password"].PrefixPosition(), password => password
.Permission(Permissions.ManageUsers)
.Permission(CommonPermissions.ManageUsers)
.Action("Index", "Admin", new { area = "OrchardCore.Settings", groupId = ResetPasswordSettingsDisplayDriver.GroupId })
.LocalNav()
)));
Expand Down
195 changes: 90 additions & 105 deletions src/OrchardCore.Modules/OrchardCore.Users/Controllers/AdminController.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public IActionResult RegistrationPending(string returnUrl = null)
[ValidateAntiForgeryToken]
public async Task<IActionResult> SendVerificationEmail(string id)
{
if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageUsers))
if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageUsers))
{
return Forbid();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public override async Task<IDisplayResult> EditAsync(ChangeEmailSettings setting
{
var user = _httpContextAccessor.HttpContext?.User;

if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageUsers))
if (!await _authorizationService.AuthorizeAsync(user, CommonPermissions.ManageUsers))
{
return null;
}
Expand All @@ -43,7 +43,7 @@ public override async Task<IDisplayResult> UpdateAsync(ChangeEmailSettings secti
{
var user = _httpContextAccessor.HttpContext?.User;

if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageUsers))
if (!await _authorizationService.AuthorizeAsync(user, CommonPermissions.ManageUsers))
{
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public override async Task<IDisplayResult> EditAsync(LoginSettings settings, Bui
{
var user = _httpContextAccessor.HttpContext?.User;

if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageUsers))
if (!await _authorizationService.AuthorizeAsync(user, CommonPermissions.ManageUsers))
{
return null;
}
Expand All @@ -38,14 +38,14 @@ public override async Task<IDisplayResult> EditAsync(LoginSettings settings, Bui
model.DisableLocalLogin = settings.DisableLocalLogin;
model.UseScriptToSyncRoles = settings.UseScriptToSyncRoles;
model.SyncRolesScript = settings.SyncRolesScript;
model.AllowChangingEmail = settings.AllowChangingEmail;
model.AllowChangingUsername = settings.AllowChangingUsername;
}).Location("Content:5").OnGroup(GroupId);
}

public override async Task<IDisplayResult> UpdateAsync(LoginSettings section, BuildEditorContext context)
{
var user = _httpContextAccessor.HttpContext?.User;

if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageUsers))
if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext?.User, CommonPermissions.ManageUsers))
{
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public override async Task<IDisplayResult> EditAsync(RegistrationSettings settin
{
var user = _httpContextAccessor.HttpContext?.User;

if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageUsers))
if (!await _authorizationService.AuthorizeAsync(user, CommonPermissions.ManageUsers))
{
return null;
}
Expand All @@ -51,7 +51,7 @@ public override async Task<IDisplayResult> UpdateAsync(RegistrationSettings sect
{
var user = _httpContextAccessor.HttpContext?.User;

if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageUsers))
if (!await _authorizationService.AuthorizeAsync(user, CommonPermissions.ManageUsers))
{
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public override async Task<IDisplayResult> EditAsync(ResetPasswordSettings setti
{
var user = _httpContextAccessor.HttpContext?.User;

if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageUsers))
if (!await _authorizationService.AuthorizeAsync(user, CommonPermissions.ManageUsers))
{
return null;
}
Expand All @@ -45,7 +45,7 @@ public override async Task<IDisplayResult> UpdateAsync(ResetPasswordSettings sec
{
var user = _httpContextAccessor.HttpContext?.User;

if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageUsers))
if (!await _authorizationService.AuthorizeAsync(user, CommonPermissions.ManageUsers))
{
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Localization;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.Notify;
using OrchardCore.DisplayManagement.Views;
using OrchardCore.Modules;
using OrchardCore.Mvc.ModelBinding;
using OrchardCore.Users.Handlers;
using OrchardCore.Users.Models;
using OrchardCore.Users.ViewModels;
using OrchardCore.Mvc.ModelBinding;
using Microsoft.Extensions.Localization;

namespace OrchardCore.Users.Drivers
{
Expand All @@ -27,7 +27,7 @@ public class UserDisplayDriver : DisplayDriver<User>
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly INotifier _notifier;
private readonly IAuthorizationService _authorizationService;
private IEnumerable<IUserEventHandler> _userEventHandlers;
private readonly IEnumerable<IUserEventHandler> _userEventHandlers;
private readonly ILogger _logger;
private readonly IHtmlLocalizer H;
private readonly IStringLocalizer S;
Expand Down Expand Up @@ -56,32 +56,36 @@ public override IDisplayResult Display(User user)
{
return Combine(
Initialize<SummaryAdminUserViewModel>("UserFields", model => model.User = user).Location("SummaryAdmin", "Header:1"),
Initialize<SummaryAdminUserViewModel>("UserInfo", model => model.User = user).Location("DetailAdmin", "Content:5"),
Initialize<SummaryAdminUserViewModel>("UserButtons", model => model.User = user).Location("SummaryAdmin", "Actions:1")
);
}

public override Task<IDisplayResult> EditAsync(User user, BuildEditorContext context)
public override async Task<IDisplayResult> EditAsync(User user, BuildEditorContext context)
{
return Task.FromResult<IDisplayResult>(Initialize<EditUserViewModel>("UserFields_Edit", async model =>
{
model.EmailConfirmed = user.EmailConfirmed;
model.IsEnabled = user.IsEnabled;
model.IsNewRequest = context.IsNew;
// The current user cannot disable themselves, nor can a user without permission to manage this user disable them.
model.IsEditingDisabled = !await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageUsers, user) ||
String.Equals(_httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier), user.UserId, StringComparison.OrdinalIgnoreCase);
})
.Location("Content:1.5")
.RenderWhen(() => _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ViewUsers, user)));
if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.EditUsers, user))
{
return null;
}

return Initialize<EditUserViewModel>("UserFields_Edit", model =>
{
model.EmailConfirmed = user.EmailConfirmed;
model.IsEnabled = user.IsEnabled;
model.IsNewRequest = context.IsNew;
// The current user cannot disable themselves, nor can a user without permission to manage this user disable them.
model.IsEditingDisabled = IsCurrentUser(user);
})
.Location("Content:1.5");
}

public override async Task<IDisplayResult> UpdateAsync(User user, UpdateEditorContext context)
{
// To prevent html injection when updating the user must meet all authorization requirements.
if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageUsers, user))
if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.EditUsers, user))
{
// When the user is only editing their profile never update this part of the user.
return Edit(user);
return await EditAsync(user, context);
}

var model = new EditUserViewModel();
Expand All @@ -90,7 +94,7 @@ public override async Task<IDisplayResult> UpdateAsync(User user, UpdateEditorCo

if (context.IsNew)
{
if (string.IsNullOrWhiteSpace(model.Password))
if (String.IsNullOrWhiteSpace(model.Password))
{
context.Updater.ModelState.AddModelError(Prefix, nameof(model.Password), S["A password is required"]);
}
Expand All @@ -106,15 +110,18 @@ public override async Task<IDisplayResult> UpdateAsync(User user, UpdateEditorCo
return await EditAsync(user, context);
}

var isEditingDisabled = !await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageUsers, user) ||
String.Equals(_httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier), user.UserId, StringComparison.OrdinalIgnoreCase);
var isEditingDisabled = IsCurrentUser(user);

if (!isEditingDisabled && !model.IsEnabled && user.IsEnabled)
{
var usersOfAdminRole = (await _userManager.GetUsersInRoleAsync(AdministratorRole)).Cast<User>();
if (usersOfAdminRole.Count() == 1 && String.Equals(user.UserId, usersOfAdminRole.First().UserId, StringComparison.OrdinalIgnoreCase))
var enabledUsersOfAdminRole = (await _userManager.GetUsersInRoleAsync(AdministratorRole))
.Cast<User>()
.Where(user => user.IsEnabled)
.ToList();

if (enabledUsersOfAdminRole.Count == 1 && user.UserId == enabledUsersOfAdminRole.First().UserId)
{
await _notifier.WarningAsync(H["Cannot disable the only administrator."]);
await _notifier.WarningAsync(H["Cannot disable the only enabled administrator."]);
}
else
{
Expand Down Expand Up @@ -145,5 +152,10 @@ public override async Task<IDisplayResult> UpdateAsync(User user, UpdateEditorCo

return await EditAsync(user, context);
}

private bool IsCurrentUser(User user)
{
return _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier) == user.UserId;
}
}
}
Loading

0 comments on commit 009daa3

Please sign in to comment.