@@ -150,14 +150,23 @@ public async Task DeleteAsync(string userId, string currentUserId)
150150 var user = await _userManager . FindByIdAsync ( userId )
151151 ?? throw new EntityNotFoundException ( nameof ( ApplicationUser ) , userId ) ;
152152
153- var admins = await _userManager . GetUsersInRoleAsync ( Constants . AuthRoles . Admin ) ;
154- if ( admins . Count == 1 && admins [ 0 ] . Id == userId )
153+ await AdminRoleGate . Lock . WaitAsync ( ) ;
154+ try
155155 {
156- throw new DomainException ( Constants . Errors . CannotDeleteLastAdmin ) ;
156+ var admins = await _userManager . GetUsersInRoleAsync ( Constants . AuthRoles . Admin ) ;
157+ if ( admins . Any ( a => a . Id == userId ) && ! admins . Any ( a => a . Id != userId && ! a . IsDisabled ) )
158+ {
159+ throw new DomainException ( Constants . Errors . CannotDeleteLastAdmin ) ;
160+ }
161+
162+ ( await _userManager . DeleteAsync ( user ) ) . ThrowIfFailed ( ) ;
163+ }
164+ finally
165+ {
166+ AdminRoleGate . Lock . Release ( ) ;
157167 }
158168
159169 var linkedPlayerId = user . PlayerId ;
160- await _userManager . DeleteAsync ( user ) ;
161170 _accessCheck . Forget ( userId ) ;
162171 _imageService . DeleteImage ( user . ProfileImage ) ;
163172 await _playerAccountImageService . ReleaseAsync ( linkedPlayerId ) ;
@@ -190,8 +199,7 @@ public async Task<UserDto> UpdateRoleAsync(string userId, string role, string cu
190199 _logger . LogInformation ( "Admin {AdminId} updated role for user {UserId} ({Username}) to {Role}" ,
191200 currentUserId , userId , user . UserName , role ) ;
192201
193- var updatedRoles = await _userManager . GetRolesAsync ( user ) ;
194- return user . ToDto ( updatedRoles ) ;
202+ return await ToDtoAsync ( user ) ;
195203 }
196204
197205 public async Task < UserDto > UpdateUserAsync ( string userId , string username , string ? email , string role , int ? playerId , string currentUserId )
@@ -214,19 +222,12 @@ public async Task<UserDto> UpdateUserAsync(string userId, string username, strin
214222 {
215223 await EnsureNotLastAdminAsync ( userId , role , currentUserId ) ;
216224
217- if ( ! string . Equals ( user . UserName , username , StringComparison . OrdinalIgnoreCase ) )
225+ var renamed = ! string . Equals ( user . UserName , username , StringComparison . OrdinalIgnoreCase ) ;
226+ if ( renamed && await _userManager . FindByNameAsync ( username ) != null )
218227 {
219- var existingUser = await _userManager . FindByNameAsync ( username ) ;
220- if ( existingUser != null )
221- {
222- throw new ValidationException ( Constants . Errors . UsernameAlreadyExists ) ;
223- }
224-
225- await _userManager . SetUserNameAsync ( user , username ) ;
228+ throw new ValidationException ( Constants . Errors . UsernameAlreadyExists ) ;
226229 }
227230
228- user . UpdateEmail ( email , user . EmailConfirmed || ! string . Equals ( user . Email , email , StringComparison . OrdinalIgnoreCase ) ) ;
229-
230231 var previousPlayerId = user . PlayerId ;
231232 if ( playerId != user . PlayerId )
232233 {
@@ -241,7 +242,13 @@ public async Task<UserDto> UpdateUserAsync(string userId, string username, strin
241242 }
242243 }
243244
244- await _userManager . UpdateAsync ( user ) ;
245+ if ( renamed )
246+ {
247+ user . UserName = username ;
248+ }
249+
250+ user . UpdateEmail ( email , user . EmailConfirmed || ! string . Equals ( user . Email , email , StringComparison . OrdinalIgnoreCase ) ) ;
251+ ( await _userManager . UpdateAsync ( user ) ) . ThrowIfFailed ( ) ;
245252 await _playerAccountImageService . RelinkAsync ( previousPlayerId , user ) ;
246253
247254 await ReplaceRoleAsync ( user , role ) ;
@@ -254,8 +261,7 @@ public async Task<UserDto> UpdateUserAsync(string userId, string username, strin
254261 _logger . LogInformation ( "Admin {AdminId} updated user {UserId} (username: {Username}, role: {Role})" ,
255262 currentUserId , userId , username , role ) ;
256263
257- var updatedRoles = await _userManager . GetRolesAsync ( user ) ;
258- return user . ToDto ( updatedRoles ) ;
264+ return await ToDtoAsync ( user ) ;
259265 }
260266
261267 public async Task < UserDto > DisableAsync ( string userId , string currentUserId )
@@ -280,8 +286,8 @@ public async Task<UserDto> DisableAsync(string userId, string currentUserId)
280286 }
281287 }
282288
283- await _userManager . SetLockoutEnabledAsync ( user , true ) ;
284- await _userManager . SetLockoutEndDateAsync ( user , DateTimeOffset . MaxValue ) ;
289+ ( await _userManager . SetLockoutEnabledAsync ( user , true ) ) . ThrowIfFailed ( ) ;
290+ ( await _userManager . SetLockoutEndDateAsync ( user , DateTimeOffset . MaxValue ) ) . ThrowIfFailed ( ) ;
285291 _accessCheck . Forget ( userId ) ;
286292 }
287293 finally
@@ -306,7 +312,7 @@ public async Task<UserDto> EnableAsync(string userId, string currentUserId)
306312 var user = await _userManager . FindByIdAsync ( userId )
307313 ?? throw new EntityNotFoundException ( nameof ( ApplicationUser ) , userId ) ;
308314
309- await _userManager . SetLockoutEndDateAsync ( user , null ) ;
315+ ( await _userManager . SetLockoutEndDateAsync ( user , null ) ) . ThrowIfFailed ( ) ;
310316 _accessCheck . Forget ( userId ) ;
311317
312318 _logger . LogInformation ( "Admin {AdminId} enabled user {UserId} ({Username})" , currentUserId , userId , user . UserName ) ;
@@ -357,7 +363,7 @@ public async Task<UserDto> ManageRoleByIdentityProviderAsync(string userId, stri
357363 }
358364
359365 user . ManageRoleByIdentityProvider ( ) ;
360- await _userManager . UpdateAsync ( user ) ;
366+ ( await _userManager . UpdateAsync ( user ) ) . ThrowIfFailed ( ) ;
361367
362368 _logger . LogInformation ( "Admin {AdminId} handed the role of user {UserId} back to the identity provider" , currentUserId , userId ) ;
363369 return await ToDtoAsync ( user ) ;
@@ -406,13 +412,13 @@ private async Task ReplaceRoleAsync(ApplicationUser user, string role)
406412
407413 if ( ! currentRoles . Contains ( role ) )
408414 {
409- await _userManager . AddToRoleAsync ( user , role ) ;
415+ ( await _userManager . AddToRoleAsync ( user , role ) ) . ThrowIfFailed ( ) ;
410416 }
411417
412418 var obsoleteRoles = currentRoles . Where ( r => r != role ) . ToList ( ) ;
413419 if ( obsoleteRoles . Count > 0 )
414420 {
415- await _userManager . RemoveFromRolesAsync ( user , obsoleteRoles ) ;
421+ ( await _userManager . RemoveFromRolesAsync ( user , obsoleteRoles ) ) . ThrowIfFailed ( ) ;
416422 }
417423 }
418424}
0 commit comments