using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; using Timeline.Auth; using Timeline.Models; using Timeline.Models.Http; using Timeline.Models.Validation; using Timeline.Services; using Timeline.Services.User; namespace Timeline.Controllers.V2 { /// /// Operations about users. /// [ApiController] [Route("v2/users")] public class UserV2Controller : V2ControllerBase { private readonly IUserService _userService; private readonly IUserPermissionService _userPermissionService; private readonly IUserDeleteService _userDeleteService; public UserV2Controller(IUserService userService, IUserPermissionService userPermissionService, IUserDeleteService userDeleteService) { _userService = userService; _userPermissionService = userPermissionService; _userDeleteService = userDeleteService; } /// /// Get all users. /// /// All user list. [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] public async Task>> ListAsync([FromQuery][PositiveInteger] int? page, [FromQuery][PositiveInteger] int? pageSize) { var p = await _userService.GetUsersV2Async(page ?? 1, pageSize ?? 20); var items = await MapListAsync(p.Items); return p.WithItems(items); } /// /// Create a new user. You have to be administrator. /// /// The new user's info. [HttpPost] [PermissionAuthorize(UserPermission.UserManagement)] [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)] public async Task> PostAsync([FromBody] HttpUserPostRequest body) { var user = await _userService.CreateUserAsync( new CreateUserParams(body.Username, body.Password) { Nickname = body.Nickname }); return CreatedAtAction("Get", new { username = body.Username }, await MapAsync(user)); } /// /// Get a user's info. /// /// Username of the user. /// User info. [HttpGet("{username}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)] public async Task> GetAsync([FromRoute][Username] string username) { var id = await _userService.GetUserIdByUsernameAsync(username); var user = await _userService.GetUserAsync(id); return await MapAsync(user); } /// /// Change a user's property. /// /// /// Username of the user to change. /// The new user info. [HttpPatch("{username}")] [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)] public async Task> PatchAsync([FromBody] HttpUserPatchRequest body, [FromRoute][Username] string username) { var userId = await _userService.GetUserIdByUsernameAsync(username); if (UserHasPermission(UserPermission.UserManagement)) { var user = await _userService.ModifyUserAsync(userId, AutoMapperMap(body)); return await MapAsync(user); } else { if (userId != GetAuthUserId()) return Forbid(); if (body.Username is not null) return Forbid(); if (body.Password is not null) return Forbid(); var user = await _userService.ModifyUserAsync(GetAuthUserId(), AutoMapperMap(body)); return await MapAsync(user); } } private const string RootUserInvalidOperationMessage = "Can't do this operation on root user."; /// /// Delete a user and all his related data. You have to be administrator. /// /// Username of the user to delete. /// Info of deletion. [HttpDelete("{username}")] [PermissionAuthorize(UserPermission.UserManagement)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)] public async Task DeleteAsync([FromRoute][Username] string username) { try { await _userDeleteService.DeleteUserAsync(username); return NoContent(); } catch (EntityNotExistException) { return NoContent(); } catch (InvalidOperationOnRootUserException) { return UnprocessableEntity(new ErrorResponse(ErrorResponse.InvalidOperation, RootUserInvalidOperationMessage)); } } [HttpPut("{username}/permissions/{permission}"), PermissionAuthorize(UserPermission.UserManagement)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)] public async Task PutUserPermissionAsync([FromRoute][Username] string username, [FromRoute] UserPermission permission) { try { var id = await _userService.GetUserIdByUsernameAsync(username); await _userPermissionService.AddPermissionToUserAsync(id, permission); return NoContent(); } catch (InvalidOperationOnRootUserException) { return UnprocessableEntity(new ErrorResponse(ErrorResponse.InvalidOperation, RootUserInvalidOperationMessage)); } } [HttpDelete("{username}/permissions/{permission}"), PermissionAuthorize(UserPermission.UserManagement)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)] public async Task DeleteUserPermissionAsync([FromRoute][Username] string username, [FromRoute] UserPermission permission) { try { var id = await _userService.GetUserIdByUsernameAsync(username); await _userPermissionService.RemovePermissionFromUserAsync(id, permission); return NoContent(); } catch (InvalidOperationOnRootUserException) { return UnprocessableEntity(new ErrorResponse(ErrorResponse.InvalidOperation, RootUserInvalidOperationMessage)); } } } }