From 5c542583d82b05a2a44cc0bf336dc6275d1dc25b Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Sun, 4 Jan 2026 22:39:26 +0300 Subject: [PATCH] Bump OCC.Users to 1.7.0 --- src/OrchardCoreContrib.Users/AdminMenu.cs | 69 +++++----- .../ClaimTypesExtended.cs | 25 ++-- .../Controllers/ImpersonationController.cs | 102 ++++++--------- .../Drivers/ImpersonationDisplayDriver.cs | 21 ++- src/OrchardCoreContrib.Users/Manifest.cs | 2 +- .../OrchardCoreContrib.Users.csproj | 4 +- src/OrchardCoreContrib.Users/Permissions.cs | 58 ++++----- src/OrchardCoreContrib.Users/README.md | 3 +- .../Services/AvatarService.cs | 12 +- .../Services/NullAvatarService.cs | 6 +- src/OrchardCoreContrib.Users/Startup.cs | 121 ++++++++---------- .../UsersPermissions.cs | 14 ++ 12 files changed, 202 insertions(+), 235 deletions(-) create mode 100644 src/OrchardCoreContrib.Users/UsersPermissions.cs diff --git a/src/OrchardCoreContrib.Users/AdminMenu.cs b/src/OrchardCoreContrib.Users/AdminMenu.cs index be5ea268..af590391 100644 --- a/src/OrchardCoreContrib.Users/AdminMenu.cs +++ b/src/OrchardCoreContrib.Users/AdminMenu.cs @@ -3,51 +3,42 @@ using OrchardCore.Modules; using OrchardCore.Mvc.Core.Utilities; using OrchardCore.Navigation; -using OrchardCoreContrib.Navigation; using OrchardCoreContrib.Users.Controllers; -namespace OrchardCoreContrib.Users -{ - using OrchardCoreContrib.Navigation; +namespace OrchardCoreContrib.Users; - /// - /// Represents an admin menu for the impersonation feature. - /// - [Feature("OrchardCoreContrib.Users.Impersonation")] - public class AdminMenu : AdminNavigationProvider - { - private readonly HttpContext _httpContext; - private readonly IStringLocalizer S; +using OrchardCoreContrib.Navigation; - /// - /// Initializes a new instance of . - /// - /// The . - public AdminMenu( - IHttpContextAccessor httpContextAccessor, - IStringLocalizer localizer) - { - _httpContext = httpContextAccessor.HttpContext; - S = localizer; - } +/// +/// Represents an admin menu for the impersonation feature. +/// +/// +/// Initializes a new instance of . +/// +/// The . +[Feature("OrchardCoreContrib.Users.Impersonation")] +public class AdminMenu( + IHttpContextAccessor httpContextAccessor, + IStringLocalizer S) : AdminNavigationProvider +{ + private readonly HttpContext _httpContext = httpContextAccessor.HttpContext; - /// - public override void BuildNavigation(NavigationBuilder builder) + /// + public override void BuildNavigation(NavigationBuilder builder) + { + var isImpersonatingClaim = _httpContext.User.FindFirst(ClaimTypesExtended.IsImpersonating); + + if (isImpersonatingClaim?.Value == "true") { - var isImpersonatingClaim = _httpContext.User.FindFirst(ClaimTypesExtended.IsImpersonating); - - if (isImpersonatingClaim?.Value == "true") - { - builder - .Add(S["Security"], NavigationConstants.AdminMenuSecurityPosition, security => security - .AddClass("security").Id("security") - .Add(S["End Impersonation"], S["End Impersonation"].PrefixPosition(), users => users - .AddClass("endImpersonation").Id("endImpersonation") - .Action(nameof(ImpersonationController.EndImpersonatation), typeof(ImpersonationController).ControllerName(), "OrchardCoreContrib.Users") - .LocalNav() - ) - ); - } + builder + .Add(S["Security"], NavigationConstants.AdminMenuSecurityPosition, security => security + .AddClass("security").Id("security") + .Add(S["End Impersonation"], S["End Impersonation"].PrefixPosition(), users => users + .AddClass("endImpersonation").Id("endImpersonation") + .Action(nameof(ImpersonationController.EndImpersonatation), typeof(ImpersonationController).ControllerName(), "OrchardCoreContrib.Users") + .LocalNav() + ) + ); } } } diff --git a/src/OrchardCoreContrib.Users/ClaimTypesExtended.cs b/src/OrchardCoreContrib.Users/ClaimTypesExtended.cs index 52fbf9b3..bf945d56 100644 --- a/src/OrchardCoreContrib.Users/ClaimTypesExtended.cs +++ b/src/OrchardCoreContrib.Users/ClaimTypesExtended.cs @@ -1,18 +1,17 @@ -namespace OrchardCoreContrib.Users +namespace OrchardCoreContrib.Users; + +/// +/// Represents a claim types that be used for impersonation process. +/// +public static class ClaimTypesExtended { /// - /// Represents a claim types that be used for impersonation process. + /// Gets the impersonator name. /// - public static class ClaimTypesExtended - { - /// - /// Gets the impersonator name. - /// - public static readonly string ImpersonatorNameIdentifier = nameof(ImpersonatorNameIdentifier); + public static readonly string ImpersonatorNameIdentifier = nameof(ImpersonatorNameIdentifier); - /// - /// Gets whether the current user is impersonated or not. - /// - public static readonly string IsImpersonating = nameof(IsImpersonating); - } + /// + /// Gets whether the current user is impersonated or not. + /// + public static readonly string IsImpersonating = nameof(IsImpersonating); } diff --git a/src/OrchardCoreContrib.Users/Controllers/ImpersonationController.cs b/src/OrchardCoreContrib.Users/Controllers/ImpersonationController.cs index 0743d9bf..86e0d664 100644 --- a/src/OrchardCoreContrib.Users/Controllers/ImpersonationController.cs +++ b/src/OrchardCoreContrib.Users/Controllers/ImpersonationController.cs @@ -2,91 +2,71 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using OrchardCore.Admin; using OrchardCore.Modules; using OrchardCore.Users; -using System.Linq; using System.Security.Claims; -using System.Threading.Tasks; -namespace OrchardCoreContrib.Users.Controllers +namespace OrchardCoreContrib.Users.Controllers; + +[Authorize] +[Feature("OrchardCoreContrib.Users.Impersonation")] +public class ImpersonationController( + SignInManager signInManager, + UserManager userManager, + IAuthorizationService authorizationService, + IOptions adminOptions, + ILogger logger) : Controller { - [Authorize] - [Feature("OrchardCoreContrib.Users.Impersonation")] - public class ImpersonationController : Controller + private readonly AdminOptions _adminOptions = adminOptions.Value; + + public async Task ImpersonateUser(string userId) { - private readonly SignInManager _signInManager; - private readonly UserManager _userManager; - private readonly IAuthorizationService _authorizationService; - private readonly AdminOptions _adminOptions; - private readonly ILogger _logger; - private readonly IStringLocalizer S; - - public ImpersonationController( - SignInManager signInManager, - UserManager userManager, - IAuthorizationService authorizationService, - IOptions adminOptions, - ILogger logger, - IStringLocalizer stringLocalizer) + if (!await authorizationService.AuthorizeAsync(User, UsersPermissions.ManageImpersonationSettings)) { - _signInManager = signInManager; - _userManager = userManager; - _authorizationService = authorizationService; - _adminOptions = adminOptions.Value; - _logger = logger; - S = stringLocalizer; + return Forbid(); } - public async Task ImpersonateUser(string userId) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageImpersonationSettings)) - { - return Forbid(); - } + var currentUserId = HttpContext.User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value; + var impersonatedUser = await userManager.FindByIdAsync(userId); + var impersonatedUserPrincipal = await signInManager.CreateUserPrincipalAsync(impersonatedUser); - var currentUserId = HttpContext.User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value; - var impersonatedUser = await _userManager.FindByIdAsync(userId); - var impersonatedUserPrincipal = await _signInManager.CreateUserPrincipalAsync(impersonatedUser); + impersonatedUserPrincipal.Identities.First().AddClaim(new Claim(ClaimTypesExtended.ImpersonatorNameIdentifier, currentUserId)); + impersonatedUserPrincipal.Identities.First().AddClaim(new Claim(ClaimTypesExtended.IsImpersonating, "true")); - impersonatedUserPrincipal.Identities.First().AddClaim(new Claim(ClaimTypesExtended.ImpersonatorNameIdentifier, currentUserId)); - impersonatedUserPrincipal.Identities.First().AddClaim(new Claim(ClaimTypesExtended.IsImpersonating, "true")); + await signInManager.SignOutAsync(); + await HttpContext.SignInAsync(IdentityConstants.ApplicationScheme, impersonatedUserPrincipal); - await _signInManager.SignOutAsync(); - await HttpContext.SignInAsync(IdentityConstants.ApplicationScheme, impersonatedUserPrincipal); + logger.LogInformation(1, "User '{0}' logged as '{1}'.", User.Identity.Name, impersonatedUser.UserName); - _logger.LogInformation(1, S["User '{0}' logged as '{1}'.", User.Identity.Name, impersonatedUser.UserName]); - - return Redirect($"~/{_adminOptions.AdminUrlPrefix}"); - } + return Redirect($"~/{_adminOptions.AdminUrlPrefix}"); + } - public async Task EndImpersonatation() + public async Task EndImpersonatation() + { + var isImpersonatingClaim = HttpContext.User.FindFirst(ClaimTypesExtended.IsImpersonating); + if (isImpersonatingClaim == null || isImpersonatingClaim?.Value != "true") { - var isImpersonatingClaim = HttpContext.User.FindFirst(ClaimTypesExtended.IsImpersonating); - if (isImpersonatingClaim == null || isImpersonatingClaim?.Value != "true") - { - return Forbid(); - } + return Forbid(); + } - var impersonatorUserId = HttpContext.User.Claims.First(c => c.Type == ClaimTypesExtended.ImpersonatorNameIdentifier).Value; + var impersonatorUserId = HttpContext.User.Claims.First(c => c.Type == ClaimTypesExtended.ImpersonatorNameIdentifier).Value; - var impersonatedUserId = HttpContext.User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value; - var impersonatedUser = await _userManager.FindByIdAsync(impersonatedUserId); - var impersonatedUserPrincipal = await _signInManager.CreateUserPrincipalAsync(impersonatedUser); + var impersonatedUserId = HttpContext.User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value; + var impersonatedUser = await userManager.FindByIdAsync(impersonatedUserId); + var impersonatedUserPrincipal = await signInManager.CreateUserPrincipalAsync(impersonatedUser); - await _signInManager.SignOutAsync(); + await signInManager.SignOutAsync(); - var impersonaterUser = await _userManager.FindByIdAsync(impersonatorUserId); - var impersonaterUserPrincipal = await _signInManager.CreateUserPrincipalAsync(impersonaterUser); + var impersonaterUser = await userManager.FindByIdAsync(impersonatorUserId); + var impersonaterUserPrincipal = await signInManager.CreateUserPrincipalAsync(impersonaterUser); - await HttpContext.SignInAsync(IdentityConstants.ApplicationScheme, impersonaterUserPrincipal); + await HttpContext.SignInAsync(IdentityConstants.ApplicationScheme, impersonaterUserPrincipal); - _logger.LogInformation(1, S["Swicthed back to the '{0}' user.", impersonaterUser.UserName]); + logger.LogInformation(1, "Swicthed back to the '{0}' user.", impersonaterUser.UserName); - return Redirect($"~/{_adminOptions.AdminUrlPrefix}"); - } + return Redirect($"~/{_adminOptions.AdminUrlPrefix}"); } } diff --git a/src/OrchardCoreContrib.Users/Drivers/ImpersonationDisplayDriver.cs b/src/OrchardCoreContrib.Users/Drivers/ImpersonationDisplayDriver.cs index 9f78c265..0883f041 100644 --- a/src/OrchardCoreContrib.Users/Drivers/ImpersonationDisplayDriver.cs +++ b/src/OrchardCoreContrib.Users/Drivers/ImpersonationDisplayDriver.cs @@ -1,4 +1,6 @@ -using OrchardCore.DisplayManagement.Handlers; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; using OrchardCore.Modules; using OrchardCore.Users.Models; @@ -7,9 +9,20 @@ namespace OrchardCoreContrib.Users.Drivers; [Feature("OrchardCoreContrib.Users.Impersonation")] -public class ImpersonationDisplayDriver : DisplayDriver +public class ImpersonationDisplayDriver( + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService) : DisplayDriver { - public override IDisplayResult Display(User user, BuildDisplayContext context) - => Initialize("ImpersonationButton", model => model.User = user) + private readonly HttpContext _httpContext = httpContextAccessor.HttpContext; + + public async override Task DisplayAsync(User user, BuildDisplayContext context) + { + if (!await authorizationService.AuthorizeAsync(_httpContext.User, UsersPermissions.ManageImpersonationSettings)) + { + return null; + } + + return Initialize("ImpersonationButton", model => model.User = user) .Location("SummaryAdmin", "Actions:2"); + } } diff --git a/src/OrchardCoreContrib.Users/Manifest.cs b/src/OrchardCoreContrib.Users/Manifest.cs index 804d8fcb..ef08a47c 100644 --- a/src/OrchardCoreContrib.Users/Manifest.cs +++ b/src/OrchardCoreContrib.Users/Manifest.cs @@ -5,7 +5,7 @@ Name = "Users", Author = ManifestConstants.Author, Website = ManifestConstants.Website, - Version = "1.5.1", + Version = "1.7.0", Category = "Security" )] diff --git a/src/OrchardCoreContrib.Users/OrchardCoreContrib.Users.csproj b/src/OrchardCoreContrib.Users/OrchardCoreContrib.Users.csproj index 50e230a1..978e56d8 100644 --- a/src/OrchardCoreContrib.Users/OrchardCoreContrib.Users.csproj +++ b/src/OrchardCoreContrib.Users/OrchardCoreContrib.Users.csproj @@ -2,10 +2,11 @@ true - 1.6.0 + 1.7.0 The Orchard Core Contrib Team Provides a list of users features such as Impersonation. + README.md BSD-3-Clause https://github.com/OrchardCoreContrib/OrchardCoreContrib.Modules/tree/main/src/OrchardCoreContrib.Users/README.md https://github.com/OrchardCoreContrib/OrchardCoreContrib.Modules @@ -26,6 +27,7 @@ + diff --git a/src/OrchardCoreContrib.Users/Permissions.cs b/src/OrchardCoreContrib.Users/Permissions.cs index 658adce6..162af4a9 100644 --- a/src/OrchardCoreContrib.Users/Permissions.cs +++ b/src/OrchardCoreContrib.Users/Permissions.cs @@ -1,44 +1,32 @@ using OrchardCore.Modules; -using OrchardCore.Security; using OrchardCore.Security.Permissions; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -namespace OrchardCoreContrib.Users +namespace OrchardCoreContrib.Users; + +/// +/// Represents a permissions that will be applied into users module. +/// +[Feature("OrchardCoreContrib.Users.Impersonation")] +public class Permissions : IPermissionProvider { - /// - /// Represents a permissions that will be applied into users module. - /// - [Feature("OrchardCoreContrib.Users.Impersonation")] - public class Permissions : IPermissionProvider - { - /// - /// Gets a permission for managing a impersonation settings. - /// - public static readonly Permission ManageImpersonationSettings = new Permission("ManageImpersonationSettings", "Manage Impersonation Settings", isSecurityCritical: true); + private readonly IEnumerable _allPermissions = + [ + UsersPermissions.ManageImpersonationSettings, + ]; - /// - public Task> GetPermissionsAsync() - { - return Task.FromResult(new[] - { - ManageImpersonationSettings - } - .AsEnumerable()); - } + /// + public Task> GetPermissionsAsync() => Task.FromResult(_allPermissions); - /// - public IEnumerable GetDefaultStereotypes() - { - return new[] + /// + public IEnumerable GetDefaultStereotypes() + { + return + [ + new PermissionStereotype { - new PermissionStereotype - { - Name = "Administrator", - Permissions = new[] { ManageImpersonationSettings, StandardPermissions.SiteOwner } - }, - }; - } + Name = "Administrator", + Permissions = _allPermissions + }, + ]; } } diff --git a/src/OrchardCoreContrib.Users/README.md b/src/OrchardCoreContrib.Users/README.md index 80bf61e3..2fbc4184 100644 --- a/src/OrchardCoreContrib.Users/README.md +++ b/src/OrchardCoreContrib.Users/README.md @@ -4,7 +4,7 @@ This module provides features for users management. ## Version -1.5.1 +1.7.0 ## Category @@ -34,6 +34,7 @@ Security | Name | Version | |---------------------------------------------------------------------------------------------|---------| +| [`OrchardCoreContrib.Users`](https://www.nuget.org/packages/OrchardCoreContrib.Users/1.7.0) | 1.7.0 | | [`OrchardCoreContrib.Users`](https://www.nuget.org/packages/OrchardCoreContrib.Users/1.6.0) | 1.6.0 | | [`OrchardCoreContrib.Users`](https://www.nuget.org/packages/OrchardCoreContrib.Users/1.5.1) | 1.5.1 | | [`OrchardCoreContrib.Users`](https://www.nuget.org/packages/OrchardCoreContrib.Users/1.5.0) | 1.5.0 | diff --git a/src/OrchardCoreContrib.Users/Services/AvatarService.cs b/src/OrchardCoreContrib.Users/Services/AvatarService.cs index 4ba696e4..23efd3f6 100644 --- a/src/OrchardCoreContrib.Users/Services/AvatarService.cs +++ b/src/OrchardCoreContrib.Users/Services/AvatarService.cs @@ -1,25 +1,17 @@ using Microsoft.Extensions.Options; using SixLabors.Fonts; using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Drawing; using SixLabors.ImageSharp.Drawing.Processing; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using System; -using System.IO; namespace OrchardCoreContrib.Users.Services; -public class AvatarService : IAvatarService +public class AvatarService(IOptions avatarOptions) : IAvatarService { - private readonly AvatarOptions _avatarOptions; - - public AvatarService(IOptions avatarOptions) - { - _avatarOptions = avatarOptions.Value; - } + private readonly AvatarOptions _avatarOptions = avatarOptions.Value; public string Generate(string userName) { diff --git a/src/OrchardCoreContrib.Users/Services/NullAvatarService.cs b/src/OrchardCoreContrib.Users/Services/NullAvatarService.cs index ad46cd9e..5bcabc10 100644 --- a/src/OrchardCoreContrib.Users/Services/NullAvatarService.cs +++ b/src/OrchardCoreContrib.Users/Services/NullAvatarService.cs @@ -1,8 +1,6 @@ -using System; - -namespace OrchardCoreContrib.Users.Services; +namespace OrchardCoreContrib.Users.Services; public class NullAvatarService : IAvatarService { - public string Generate(string userName) => String.Empty; + public string Generate(string userName) => string.Empty; } diff --git a/src/OrchardCoreContrib.Users/Startup.cs b/src/OrchardCoreContrib.Users/Startup.cs index eaced249..b318c0dc 100644 --- a/src/OrchardCoreContrib.Users/Startup.cs +++ b/src/OrchardCoreContrib.Users/Startup.cs @@ -13,84 +13,73 @@ using OrchardCoreContrib.Users.Controllers; using OrchardCoreContrib.Users.Drivers; using OrchardCoreContrib.Users.Services; -using System; -using System.Linq; -using System.Threading.Tasks; -namespace OrchardCoreContrib.Users +namespace OrchardCoreContrib.Users; + +/// +/// Represents an entry point to register the impersonation required services. +/// +/// +/// Initializes a new instance of . +/// +/// The adminOptions) : StartupBase { - /// - /// Represents an entry point to register the impersonation required services. - /// - [Feature("OrchardCoreContrib.Users.Impersonation")] - public class ImpersonationStartup : StartupBase - { - private readonly AdminOptions _adminOptions; + private readonly AdminOptions _adminOptions = adminOptions.Value; - /// - /// Initializes a new instance of . - /// - /// The - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - routes.MapAreaControllerRoute( - name: "UsersImpersonateUser", - areaName: "OrchardCoreContrib.Users", - pattern: _adminOptions.AdminUrlPrefix + "/Users/Impersonate", - defaults: new { controller = typeof(ImpersonationController).ControllerName(), action = nameof(ImpersonationController.ImpersonateUser) } - ); - } + /// + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + routes.MapAreaControllerRoute( + name: "UsersImpersonateUser", + areaName: "OrchardCoreContrib.Users", + pattern: _adminOptions.AdminUrlPrefix + "/Users/Impersonate", + defaults: new + { + controller = typeof(ImpersonationController).ControllerName(), + action = nameof(ImpersonationController.ImpersonateUser) + } + ); } +} + +/// +/// Represents an entry point to register the user avatar required services. +/// +[Feature("OrchardCoreContrib.Users.Avatar")] +public class UserAvatarStartup(IShellConfiguration shellConfiguration) : StartupBase +{ - /// - /// Represents an entry point to register the user avatar required services. - /// - [Feature("OrchardCoreContrib.Users.Avatar")] - public class UserAvatarStartup : StartupBase + /// + public override void ConfigureServices(IServiceCollection services) { - private readonly IShellConfiguration _shellConfiguration; + services.AddScoped(); - public UserAvatarStartup(IShellConfiguration shellConfiguration) - { - _shellConfiguration = shellConfiguration; - } - - /// - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - - services.Configure(_shellConfiguration.GetSection("OrchardCoreContrib_Users_AvatarOptions")); - } + services.Configure(shellConfiguration.GetSection("OrchardCoreContrib_Users_AvatarOptions")); } } diff --git a/src/OrchardCoreContrib.Users/UsersPermissions.cs b/src/OrchardCoreContrib.Users/UsersPermissions.cs new file mode 100644 index 00000000..a9398155 --- /dev/null +++ b/src/OrchardCoreContrib.Users/UsersPermissions.cs @@ -0,0 +1,14 @@ +using OrchardCore.Security.Permissions; + +namespace OrchardCoreContrib.Users; + +/// +/// Provides predefined permissions related to Users module. +/// +public class UsersPermissions +{ + /// + /// Gets a permission for managing a impersonation settings. + /// + public static readonly Permission ManageImpersonationSettings = new("ManageImpersonationSettings", "Manage Impersonation Settings", isSecurityCritical: true); +}