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.
}
}
}