using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using API.Constants; using API.DTOs; using API.Entities; using API.Extensions; using API.Interfaces; using AutoMapper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace API.Controllers { public class AccountController : BaseApiController { private readonly UserManager _userManager; private readonly SignInManager _signInManager; private readonly ITokenService _tokenService; private readonly IUnitOfWork _unitOfWork; private readonly ILogger _logger; private readonly IMapper _mapper; public AccountController(UserManager userManager, SignInManager signInManager, ITokenService tokenService, IUnitOfWork unitOfWork, ILogger logger, IMapper mapper) { _userManager = userManager; _signInManager = signInManager; _tokenService = tokenService; _unitOfWork = unitOfWork; _logger = logger; _mapper = mapper; } [Authorize(Policy = "RequireAdminRole")] [HttpPost("reset-password")] public async Task UpdatePassword(ResetPasswordDto resetPasswordDto) { _logger.LogInformation($"{User.GetUsername()} is changing {resetPasswordDto.UserName}'s password."); var user = await _userManager.Users.SingleAsync(x => x.UserName == resetPasswordDto.UserName); var result = await _userManager.RemovePasswordAsync(user); if (!result.Succeeded) return BadRequest("Unable to update password"); result = await _userManager.AddPasswordAsync(user, resetPasswordDto.Password); if (!result.Succeeded) return BadRequest("Unable to update password"); return Ok($"{resetPasswordDto.UserName}'s Password has been reset."); } [HttpPost("register")] public async Task> Register(RegisterDto registerDto) { if (await _userManager.Users.AnyAsync(x => x.UserName == registerDto.Username)) { return BadRequest("Username is taken."); } var user = _mapper.Map(registerDto); user.UserPreferences ??= new AppUserPreferences(); var result = await _userManager.CreateAsync(user, registerDto.Password); if (!result.Succeeded) return BadRequest(result.Errors); var role = registerDto.IsAdmin ? PolicyConstants.AdminRole : PolicyConstants.PlebRole; var roleResult = await _userManager.AddToRoleAsync(user, role); if (!roleResult.Succeeded) return BadRequest(result.Errors); // When we register an admin, we need to grant them access to all Libraries. if (registerDto.IsAdmin) { _logger.LogInformation($"{user.UserName} is being registered as admin. Granting access to all libraries."); var libraries = (await _unitOfWork.LibraryRepository.GetLibrariesAsync()).ToList(); foreach (var lib in libraries) { lib.AppUsers ??= new List(); lib.AppUsers.Add(user); } if (libraries.Any() && !await _unitOfWork.Complete()) _logger.LogError("There was an issue granting library access. Please do this manually."); } return new UserDto { Username = user.UserName, Token = await _tokenService.CreateToken(user), Preferences = _mapper.Map(user.UserPreferences) }; } [HttpPost("login")] public async Task> Login(LoginDto loginDto) { var user = await _userManager.Users .Include(u => u.UserPreferences) .SingleOrDefaultAsync(x => x.NormalizedUserName == loginDto.Username.ToUpper()); if (user == null) return Unauthorized("Invalid username"); var result = await _signInManager .CheckPasswordSignInAsync(user, loginDto.Password, false); if (!result.Succeeded) return Unauthorized(); // Update LastActive on account user.LastActive = DateTime.Now; user.UserPreferences ??= new AppUserPreferences(); _unitOfWork.UserRepository.Update(user); await _unitOfWork.Complete(); _logger.LogInformation($"{user.UserName} logged in at {user.LastActive}"); return new UserDto { Username = user.UserName, Token = await _tokenService.CreateToken(user), Preferences = _mapper.Map(user.UserPreferences) }; } } }