aboutsummaryrefslogtreecommitdiff
path: root/BackEnd
diff options
context:
space:
mode:
Diffstat (limited to 'BackEnd')
-rw-r--r--BackEnd/Timeline/Controllers/V2/TimelineBookmarkV2Controller.cs (renamed from BackEnd/Timeline/Controllers/TimelineBookmarkV2Controller.cs)4
-rw-r--r--BackEnd/Timeline/Controllers/V2/TimelinePostV2Controller.cs (renamed from BackEnd/Timeline/Controllers/TimelinePostV2Controller.cs)4
-rw-r--r--BackEnd/Timeline/Controllers/V2/TimelineV2Controller.cs (renamed from BackEnd/Timeline/Controllers/TimelineV2Controller.cs)4
-rw-r--r--BackEnd/Timeline/Controllers/V2/UserV2Controller.cs181
-rw-r--r--BackEnd/Timeline/Controllers/V2/V2ControllerBase.cs28
-rw-r--r--BackEnd/Timeline/Services/User/IUserService.cs5
-rw-r--r--BackEnd/Timeline/Services/User/UserService.cs15
-rw-r--r--BackEnd/Timeline/Timeline.csproj6
8 files changed, 239 insertions, 8 deletions
diff --git a/BackEnd/Timeline/Controllers/TimelineBookmarkV2Controller.cs b/BackEnd/Timeline/Controllers/V2/TimelineBookmarkV2Controller.cs
index 2b31f43e..a23a061b 100644
--- a/BackEnd/Timeline/Controllers/TimelineBookmarkV2Controller.cs
+++ b/BackEnd/Timeline/Controllers/V2/TimelineBookmarkV2Controller.cs
@@ -10,11 +10,11 @@ using Timeline.Services.Api;
using Timeline.Services.Timeline;
using Timeline.Services.User;
-namespace Timeline.Controllers
+namespace Timeline.Controllers.V2
{
[ApiController]
[Route("v2/users/{username}/bookmarks")]
- public class TimelineBookmarkV2Controller : MyControllerBase
+ public class TimelineBookmarkV2Controller : V2ControllerBase
{
private readonly IUserService _userService;
private readonly ITimelineService _timelineService;
diff --git a/BackEnd/Timeline/Controllers/TimelinePostV2Controller.cs b/BackEnd/Timeline/Controllers/V2/TimelinePostV2Controller.cs
index c80cda17..aa839abf 100644
--- a/BackEnd/Timeline/Controllers/TimelinePostV2Controller.cs
+++ b/BackEnd/Timeline/Controllers/V2/TimelinePostV2Controller.cs
@@ -14,11 +14,11 @@ using Timeline.Services.Timeline;
using Timeline.Services.User;
using Timeline.SignalRHub;
-namespace Timeline.Controllers
+namespace Timeline.Controllers.V2
{
[ApiController]
[Route("v2/timelines/{owner}/{timeline}/posts")]
- public class TimelinePostV2Controller : MyControllerBase
+ public class TimelinePostV2Controller : V2ControllerBase
{
private readonly ITimelineService _timelineService;
private readonly ITimelinePostService _postService;
diff --git a/BackEnd/Timeline/Controllers/TimelineV2Controller.cs b/BackEnd/Timeline/Controllers/V2/TimelineV2Controller.cs
index 9811cbed..393446f7 100644
--- a/BackEnd/Timeline/Controllers/TimelineV2Controller.cs
+++ b/BackEnd/Timeline/Controllers/V2/TimelineV2Controller.cs
@@ -10,11 +10,11 @@ using Timeline.Services.Mapper;
using Timeline.Services.Timeline;
using Timeline.Services.User;
-namespace Timeline.Controllers
+namespace Timeline.Controllers.V2
{
[ApiController]
[Route("v2/timelines")]
- public class TimelineV2Controller : MyControllerBase
+ public class TimelineV2Controller : V2ControllerBase
{
private ITimelineService _timelineService;
private IGenericMapper _mapper;
diff --git a/BackEnd/Timeline/Controllers/V2/UserV2Controller.cs b/BackEnd/Timeline/Controllers/V2/UserV2Controller.cs
new file mode 100644
index 00000000..e556bf8e
--- /dev/null
+++ b/BackEnd/Timeline/Controllers/V2/UserV2Controller.cs
@@ -0,0 +1,181 @@
+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.Mapper;
+using Timeline.Services.User;
+
+namespace Timeline.Controllers.V2
+{
+ /// <summary>
+ /// Operations about users.
+ /// </summary>
+ [ApiController]
+ [Route("v2/users")]
+ public class UserV2Controller : V2ControllerBase
+ {
+ private readonly IUserService _userService;
+ private readonly IUserPermissionService _userPermissionService;
+ private readonly IUserDeleteService _userDeleteService;
+ private readonly IGenericMapper _mapper;
+
+ public UserV2Controller(IUserService userService, IUserPermissionService userPermissionService, IUserDeleteService userDeleteService, IGenericMapper mapper)
+ {
+ _userService = userService;
+ _userPermissionService = userPermissionService;
+ _userDeleteService = userDeleteService;
+ _mapper = mapper;
+ }
+
+ /// <summary>
+ /// Get all users.
+ /// </summary>
+ /// <returns>All user list.</returns>
+ [HttpGet]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public async Task<ActionResult<Page<HttpUser>>> ListAsync([FromQuery][PositiveInteger] int? page, [FromQuery][PositiveInteger] int? pageSize)
+ {
+ var p = await _userService.GetUsersV2Async(page ?? 1, pageSize ?? 20);
+ var items = await _mapper.MapListAsync<HttpUser>(p.Items, Url, User);
+ return p.WithItems(items);
+ }
+
+ /// <summary>
+ /// Create a new user. You have to be administrator.
+ /// </summary>
+ /// <returns>The new user's info.</returns>
+ [HttpPost]
+ [PermissionAuthorize(UserPermission.UserManagement)]
+ [ProducesResponseType(StatusCodes.Status201Created)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
+ public async Task<ActionResult<HttpUser>> 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 _mapper.MapAsync<HttpUser>(user, Url, User));
+ }
+
+ /// <summary>
+ /// Get a user's info.
+ /// </summary>
+ /// <param name="username">Username of the user.</param>
+ /// <returns>User info.</returns>
+ [HttpGet("{username}")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
+ public async Task<ActionResult<HttpUser>> GetAsync([FromRoute][Username] string username)
+ {
+ var id = await _userService.GetUserIdByUsernameAsync(username);
+ var user = await _userService.GetUserAsync(id);
+ return await _mapper.MapAsync<HttpUser>(user, Url, User);
+ }
+
+ /// <summary>
+ /// Change a user's property.
+ /// </summary>
+ /// <param name="body"></param>
+ /// <param name="username">Username of the user to change.</param>
+ /// <returns>The new user info.</returns>
+ [HttpPatch("{username}")]
+ [Authorize]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
+ public async Task<ActionResult<HttpUser>> Patch([FromBody] HttpUserPatchRequest body, [FromRoute][Username] string username)
+ {
+ var userId = await _userService.GetUserIdByUsernameAsync(username);
+ if (UserHasPermission(UserPermission.UserManagement))
+ {
+ var user = await _userService.ModifyUserAsync(userId, _mapper.AutoMapperMap<ModifyUserParams>(body));
+ return await _mapper.MapAsync<HttpUser>(user, Url, 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(), _mapper.AutoMapperMap<ModifyUserParams>(body));
+ return await _mapper.MapAsync<HttpUser>(user, Url, User);
+ }
+ }
+
+ /// <summary>
+ /// Delete a user and all his related data. You have to be administrator.
+ /// </summary>
+ /// <param name="username">Username of the user to delete.</param>
+ /// <returns>Info of deletion.</returns>
+ [HttpDelete("{username}")]
+ [PermissionAuthorize(UserPermission.UserManagement)]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
+ public async Task<ActionResult<CommonDeleteResponse>> Delete([FromRoute][Username] string username)
+ {
+ try
+ {
+ await _userDeleteService.DeleteUserAsync(username);
+ return NoContent();
+ }
+ catch (InvalidOperationOnRootUserException)
+ {
+ return UnprocessableEntity();
+ }
+ }
+
+ [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<ActionResult<CommonResponse>> PutUserPermission([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();
+ }
+ }
+
+ [HttpDelete("{username}/permissions/{permission}"), PermissionAuthorize(UserPermission.UserManagement)]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
+ public async Task<ActionResult<CommonResponse>> DeleteUserPermission([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();
+ }
+ }
+ }
+}
diff --git a/BackEnd/Timeline/Controllers/V2/V2ControllerBase.cs b/BackEnd/Timeline/Controllers/V2/V2ControllerBase.cs
new file mode 100644
index 00000000..54b9c7c9
--- /dev/null
+++ b/BackEnd/Timeline/Controllers/V2/V2ControllerBase.cs
@@ -0,0 +1,28 @@
+using System;
+using Microsoft.AspNetCore.Mvc;
+using Timeline.Auth;
+using Timeline.Services.User;
+
+namespace Timeline.Controllers.V2
+{
+ public class V2ControllerBase : ControllerBase
+ {
+ #region auth
+ protected bool UserHasPermission(UserPermission permission)
+ {
+ return User.HasPermission(permission);
+ }
+
+ protected long? GetOptionalAuthUserId()
+ {
+ return User.GetOptionalUserId();
+ }
+
+ protected long GetAuthUserId()
+ {
+ return GetOptionalAuthUserId() ?? throw new InvalidOperationException(Resource.ExceptionNoUserId);
+ }
+ #endregion
+ }
+}
+
diff --git a/BackEnd/Timeline/Services/User/IUserService.cs b/BackEnd/Timeline/Services/User/IUserService.cs
index 6ea9a4d2..efb61ccd 100644
--- a/BackEnd/Timeline/Services/User/IUserService.cs
+++ b/BackEnd/Timeline/Services/User/IUserService.cs
@@ -2,7 +2,8 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Timeline.Entities;
-
+using Timeline.Models;
+
namespace Timeline.Services.User
{
public interface IUserService
@@ -46,6 +47,8 @@ namespace Timeline.Services.User
/// <returns>The user info of users.</returns>
Task<List<UserEntity>> GetUsersAsync();
+ Task<Page<UserEntity>> GetUsersV2Async(int page, int pageSize);
+
/// <summary>
/// Create a user with given info.
/// </summary>
diff --git a/BackEnd/Timeline/Services/User/UserService.cs b/BackEnd/Timeline/Services/User/UserService.cs
index d5ee9a2f..a20076d6 100644
--- a/BackEnd/Timeline/Services/User/UserService.cs
+++ b/BackEnd/Timeline/Services/User/UserService.cs
@@ -6,6 +6,7 @@ using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Timeline.Entities;
+using Timeline.Models;
using Timeline.Models.Validation;
using Timeline.Services.Token;
@@ -266,6 +267,18 @@ namespace Timeline.Services.User
_logger.LogInformation(Resource.LogChangePassowrd, entity.Username, id);
await _userTokenService.RevokeAllTokenByUserIdAsync(id);
- }
+ }
+
+ public async Task<Page<UserEntity>> GetUsersV2Async(int page, int pageSize)
+ {
+ if (page <= 0) throw new ArgumentOutOfRangeException(nameof(page));
+ if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize));
+
+ var items = await _database.Users.Skip((page - 1) * pageSize).Take(pageSize).ToListAsync();
+
+ var totalCount = await _database.Users.CountAsync();
+
+ return new Page<UserEntity>(page, pageSize, totalCount, items);
+ }
}
}
diff --git a/BackEnd/Timeline/Timeline.csproj b/BackEnd/Timeline/Timeline.csproj
index ee14de25..5db7a613 100644
--- a/BackEnd/Timeline/Timeline.csproj
+++ b/BackEnd/Timeline/Timeline.csproj
@@ -199,4 +199,10 @@
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
+ <ItemGroup>
+ <None Remove="Controllers\V2\" />
+ </ItemGroup>
+ <ItemGroup>
+ <Folder Include="Controllers\V2\" />
+ </ItemGroup>
</Project> \ No newline at end of file