using AutoMapper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using System.Linq; using System.Threading.Tasks; using Timeline.Auth; using Timeline.Helpers; using Timeline.Models; using Timeline.Models.Http; using Timeline.Models.Validation; using Timeline.Services; using Timeline.Services.Exceptions; using static Timeline.Resources.Controllers.UserController; using static Timeline.Resources.Messages; namespace Timeline.Controllers { /// /// Operations about users. /// [ApiController] [ProducesErrorResponseType(typeof(CommonResponse))] public class UserController : Controller { private readonly ILogger _logger; private readonly IUserService _userService; private readonly IUserDeleteService _userDeleteService; private readonly IMapper _mapper; /// public UserController(ILogger logger, IUserService userService, IUserDeleteService userDeleteService, IMapper mapper) { _logger = logger; _userService = userService; _userDeleteService = userDeleteService; _mapper = mapper; } private UserInfo ConvertToUserInfo(User user) => _mapper.Map(user); /// /// Get all users. /// /// All user list. [HttpGet("users")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> List() { var users = await _userService.GetUsers(); var result = users.Select(u => ConvertToUserInfo(u)).ToArray(); return Ok(result); } /// /// Get a user's info. /// /// Username of the user. /// User info. [HttpGet("users/{username}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> Get([FromRoute][Username] string username) { try { var user = await _userService.GetUserByUsername(username); return Ok(ConvertToUserInfo(user)); } catch (UserNotExistException e) { _logger.LogInformation(e, Log.Format(LogGetUserNotExist, ("Username", username))); return NotFound(ErrorResponse.UserCommon.NotExist()); } } /// /// Change a user's property. /// /// /// Username of the user to change. /// The new user info. [HttpPatch("users/{username}"), Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> Patch([FromBody] UserPatchRequest body, [FromRoute][Username] string username) { if (this.IsAdministrator()) { try { var user = await _userService.ModifyUser(username, _mapper.Map(body)); return Ok(ConvertToUserInfo(user)); } catch (UserNotExistException e) { _logger.LogInformation(e, Log.Format(LogPatchUserNotExist, ("Username", username))); return NotFound(ErrorResponse.UserCommon.NotExist()); } catch (EntityAlreadyExistException e) when (e.EntityName == EntityNames.User) { return BadRequest(ErrorResponse.UserController.UsernameConflict()); } } else { if (User.Identity.Name != username) return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.CustomMessage_Forbid(Common_Forbid_NotSelf)); if (body.Username != null) return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.CustomMessage_Forbid(UserController_Patch_Forbid_Username)); if (body.Password != null) return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.CustomMessage_Forbid(UserController_Patch_Forbid_Password)); if (body.Administrator != null) return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.CustomMessage_Forbid(UserController_Patch_Forbid_Administrator)); var user = await _userService.ModifyUser(this.GetUserId(), _mapper.Map(body)); return Ok(ConvertToUserInfo(user)); } } /// /// Delete a user and all his related data. You have to be administrator. /// /// Username of the user to delete. /// Info of deletion. [HttpDelete("users/{username}"), AdminAuthorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task> Delete([FromRoute][Username] string username) { var delete = await _userDeleteService.DeleteUser(username); if (delete) return Ok(CommonDeleteResponse.Delete()); else return Ok(CommonDeleteResponse.NotExist()); } /// /// Create a new user. You have to be administrator. /// /// The new user's info. [HttpPost("userop/createuser"), AdminAuthorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task> CreateUser([FromBody] CreateUserRequest body) { try { var user = await _userService.CreateUser(_mapper.Map(body)); return Ok(ConvertToUserInfo(user)); } catch (EntityAlreadyExistException e) when (e.EntityName == EntityNames.User) { return BadRequest(ErrorResponse.UserController.UsernameConflict()); } } /// /// Change password with old password. /// [HttpPost("userop/changepassword"), Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task ChangePassword([FromBody] ChangePasswordRequest request) { try { await _userService.ChangePassword(this.GetUserId(), request.OldPassword, request.NewPassword); return Ok(); } catch (BadPasswordException e) { _logger.LogInformation(e, Log.Format(LogChangePasswordBadPassword, ("Username", User.Identity.Name), ("Old Password", request.OldPassword))); return BadRequest(ErrorResponse.UserController.ChangePassword_BadOldPassword()); } // User can't be non-existent or the token is bad. } } }