using System.Threading.Tasks; using BTCPayServer.Data; using BTCPayServer.Security; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; namespace BTCPayServer.Plugins.Impersonation; public class ImpersonationPermissionHandler(IServiceScopeFactory services) : IPermissionHandler { public async Task HandleAsync(AuthorizationHandlerContext authContext, PermissionAuthorizationContext permContext) { if (permContext.Permission.Policy == ImpersonationPlugin.CanImpersonateUser) { var isAdmin = authContext.User.IsInRole(Roles.ServerAdmin); if (await CanImpersonateUser(permContext, isAdmin)) authContext.Succeed(permContext.Requirement); } } /// /// Determines if the current user can impersonate the target user. /// Rules: /// - User can always impersonate themselves /// - Non-admin users cannot impersonate others /// - ServerAdmins users can impersonate any user except other ServerAdmins /// - Returns false if the target user does not exist /// /// /// /// private async Task CanImpersonateUser(PermissionAuthorizationContext permContext, bool isAdmin) { if (permContext.Permission.Scope is not string impersonatedUserId) return false; if (permContext.UserId == impersonatedUserId) return true; if (!isAdmin) return false; await using var scope = services.CreateAsyncScope(); var userManager = scope.ServiceProvider.GetRequiredService>(); var impersonatedUser = await userManager.FindByIdAsync(impersonatedUserId); if (impersonatedUser is null) return false; return !await userManager.IsInRoleAsync(impersonatedUser, Roles.ServerAdmin); } }