diff --git a/API/Controllers/AccountController.cs b/API/Controllers/AccountController.cs index 888be5eff..0b47aa526 100644 --- a/API/Controllers/AccountController.cs +++ b/API/Controllers/AccountController.cs @@ -509,6 +509,21 @@ public class AccountController : BaseApiController _unitOfWork.UserRepository.Update(user); } + // Check if email is changing for a non-admin user + var isUpdatingAnotherAccount = user.Id != adminUser.Id; + if (isUpdatingAnotherAccount && !string.IsNullOrEmpty(dto.Email) && user.Email != dto.Email) + { + // Validate username change + var errors = await _accountService.ValidateEmail(dto.Email); + if (errors.Any()) return BadRequest(await _localizationService.Translate(User.GetUserId(), "email-taken")); + + user.Email = dto.Email; + user.EmailConfirmed = true; // When an admin performs the flow, we assume the email address is able to receive data + + await _userManager.UpdateNormalizedEmailAsync(user); + _unitOfWork.UserRepository.Update(user); + } + // Update roles var existingRoles = await _userManager.GetRolesAsync(user); var hasAdminRole = dto.Roles.Contains(PolicyConstants.AdminRole); @@ -612,8 +627,7 @@ public class AccountController : BaseApiController if (adminUser == null) return Unauthorized(await _localizationService.Translate(userId, "permission-denied")); dto.Email = dto.Email.Trim(); - if (string.IsNullOrEmpty(dto.Email)) - return BadRequest(await _localizationService.Translate(userId, "invalid-payload")); + if (string.IsNullOrEmpty(dto.Email)) return BadRequest(await _localizationService.Translate(userId, "invalid-payload")); _logger.LogInformation("{User} is inviting {Email} to the server", adminUser.UserName, dto.Email); @@ -623,7 +637,7 @@ public class AccountController : BaseApiController { var invitedUser = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email); if (await _userManager.IsEmailConfirmedAsync(invitedUser!)) - return BadRequest(await _localizationService.Translate(User.GetUserId(), "user-already-registered", invitedUser.UserName)); + return BadRequest(await _localizationService.Translate(User.GetUserId(), "user-already-registered", invitedUser!.UserName)); return BadRequest(await _localizationService.Translate(User.GetUserId(), "user-already-invited")); } diff --git a/API/DTOs/Account/UpdateUserDto.cs b/API/DTOs/Account/UpdateUserDto.cs index bda664bdb..ef19973f5 100644 --- a/API/DTOs/Account/UpdateUserDto.cs +++ b/API/DTOs/Account/UpdateUserDto.cs @@ -18,4 +18,8 @@ public record UpdateUserDto /// An Age Rating which will limit the account to seeing everything equal to or below said rating. /// public AgeRestrictionDto AgeRestriction { get; init; } = default!; + /// + /// Email of the user + /// + public string? Email { get; set; } = default!; } diff --git a/API/I18N/en.json b/API/I18N/en.json index 8781a8603..418427111 100644 --- a/API/I18N/en.json +++ b/API/I18N/en.json @@ -18,6 +18,7 @@ "age-restriction-update": "There was an error updating the age restriction", "no-user": "User does not exist", "username-taken": "Username already taken", + "email-taken": "Email already in use", "user-already-confirmed": "User is already confirmed", "generic-user-update": "There was an exception when updating the user", "manual-setup-fail": "Manual setup is unable to be completed. Please cancel and recreate the invite", diff --git a/API/Services/AccountService.cs b/API/Services/AccountService.cs index 71dc0f3b6..241198811 100644 --- a/API/Services/AccountService.cs +++ b/API/Services/AccountService.cs @@ -95,12 +95,12 @@ public class AccountService : IAccountService public async Task> ValidateEmail(string email) { var user = await _unitOfWork.UserRepository.GetUserByEmailAsync(email); - if (user == null) return Array.Empty(); + if (user == null) return []; - return new List() - { + return + [ new ApiException(400, "Email is already registered") - }; + ]; } /// diff --git a/UI/Web/src/app/admin/edit-user/edit-user.component.html b/UI/Web/src/app/admin/edit-user/edit-user.component.html index 1d9db290c..ef0e608cb 100644 --- a/UI/Web/src/app/admin/edit-user/edit-user.component.html +++ b/UI/Web/src/app/admin/edit-user/edit-user.component.html @@ -1,76 +1,90 @@ - + + + + + diff --git a/UI/Web/src/app/admin/edit-user/edit-user.component.ts b/UI/Web/src/app/admin/edit-user/edit-user.component.ts index b460e82f8..4de2e5205 100644 --- a/UI/Web/src/app/admin/edit-user/edit-user.component.ts +++ b/UI/Web/src/app/admin/edit-user/edit-user.component.ts @@ -50,7 +50,6 @@ export class EditUserComponent implements OnInit { this.userForm.addControl('email', new FormControl(this.member.email, [Validators.required, Validators.email])); this.userForm.addControl('username', new FormControl(this.member.username, [Validators.required, Validators.pattern(AllowedUsernameCharacters)])); - this.userForm.get('email')?.disable(); this.selectedRestriction = this.member.ageRestriction; this.cdRef.markForCheck(); }