From cdc93d041a2e3b544f8e5214b94b4092f922f52f Mon Sep 17 00:00:00 2001 From: Kawa Date: Thu, 11 Jun 2026 10:10:00 +0200 Subject: [PATCH] Fix role change silently failing via @bind The role @foreach (var role in Enum.GetValues()) { - + } @@ -193,16 +192,14 @@ else } } - private async Task OnRoleChange(AppUser user, ChangeEventArgs e) + // Bound via @bind:after, so user.Role already holds the newly-selected value when this runs. + private async Task PersistRoleAsync(AppUser user) { - if (!Enum.TryParse(e.Value?.ToString(), out var newRole)) return; try { - var oldRole = user.Role; - await UserService.UpdateRoleAsync(user.Id, newRole); - user.Role = newRole; + var oldRole = await UserService.UpdateRoleAsync(user.Id, user.Role); await Audit.LogAsync("RoleChanged", "", Array.Empty(), - $"Changed role for {user.Email} ({user.DisplayName}) from {oldRole} to {newRole}."); + $"Changed role for {user.Email} ({user.DisplayName}) from {oldRole} to {user.Role}."); _message = string.Format(T["usermgmt.msg.roleupdated"], user.DisplayName); _isError = false; } diff --git a/Services/Auth/IUserService.cs b/Services/Auth/IUserService.cs index 1dbbb29..c6efc59 100644 --- a/Services/Auth/IUserService.cs +++ b/Services/Auth/IUserService.cs @@ -11,7 +11,9 @@ public interface IUserService Task GetByEmailAsync(string email); Task> GetAllAsync(); - Task UpdateRoleAsync(string userId, UserRole role); + /// Persist a new role for the user. Returns the previous role (read from the store). + /// No user matches . + Task UpdateRoleAsync(string userId, UserRole role); Task DeleteAsync(string userId); /// Create a local password-based account. First user ever becomes Admin. diff --git a/Services/Auth/UserService.cs b/Services/Auth/UserService.cs index b289d2b..f5172a1 100644 --- a/Services/Auth/UserService.cs +++ b/Services/Auth/UserService.cs @@ -1,5 +1,6 @@ using System.Security.Claims; using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; using SharepointToolbox.Web.Core.Models; using SharepointToolbox.Web.Infrastructure.Persistence; @@ -9,11 +10,13 @@ public class UserService : IUserService { private readonly UserRepository _repo; private readonly IPasswordHasher _hasher; + private readonly ILogger _logger; - public UserService(UserRepository repo, IPasswordHasher hasher) + public UserService(UserRepository repo, IPasswordHasher hasher, ILogger logger) { _repo = repo; _hasher = hasher; + _logger = logger; } public async Task ProvisionAsync(ClaimsPrincipal principal) @@ -56,13 +59,23 @@ public class UserService : IUserService public Task> GetAllAsync() => _repo.LoadAsync(); - public async Task UpdateRoleAsync(string userId, UserRole role) + public async Task UpdateRoleAsync(string userId, UserRole role) { var users = (await _repo.LoadAsync()).ToList(); var user = users.FirstOrDefault(u => u.Id == userId) - ?? throw new KeyNotFoundException($"User {userId} not found."); + ?? throw new KeyNotFoundException($"User '{userId}' not found among {users.Count} stored users."); + + var oldRole = user.Role; user.Role = role; await _repo.UpsertAsync(user); + + // Verify the write landed by re-reading the row from disk. + var persisted = (await _repo.LoadAsync()).FirstOrDefault(u => u.Id == user.Id)?.Role; + _logger.LogInformation( + "UpdateRoleAsync: {Email} (id {Id}) {OldRole} → {NewRole}; persisted value now {Persisted}.", + user.Email, user.Id, oldRole, role, persisted); + + return oldRole; } public Task DeleteAsync(string userId) => _repo.DeleteAsync(userId);