From 0accc9f09d0aaf2292cb94e3c4e438c3f76f89e5 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 5 May 2021 15:58:40 +0800 Subject: refactor: ... --- .../HttpClientTimelineExtensions.cs | 4 +- .../Timeline.Tests/IntegratedTests/TimelineTest.cs | 12 ++-- .../IntegratedTests/UserPermissionTest.cs | 2 +- BackEnd/Timeline/Auth/PrincipalExtensions.cs | 8 ++- .../ActionResultControllerExtensions.cs | 29 --------- .../Controllers/BookmarkTimelineController.cs | 10 +-- .../Controllers/ControllerAuthExtensions.cs | 25 ------- .../Controllers/HighlightTimelineController.cs | 6 +- BackEnd/Timeline/Controllers/MyControllerBase.cs | 76 ++++++++++++++++++++++ BackEnd/Timeline/Controllers/Resource.Designer.cs | 18 +++++ BackEnd/Timeline/Controllers/Resource.resx | 6 ++ BackEnd/Timeline/Controllers/SearchController.cs | 2 +- BackEnd/Timeline/Controllers/TimelineController.cs | 59 ++++++++++------- .../Timeline/Controllers/TimelinePostController.cs | 30 ++++----- BackEnd/Timeline/Controllers/TokenController.cs | 14 ++-- .../Timeline/Controllers/UserAvatarController.cs | 10 +-- BackEnd/Timeline/Controllers/UserController.cs | 54 ++++++++------- .../Filters/CatchEntityNotExistExceptionFilter.cs | 10 ++- .../Timeline/Filters/NotEntityDeleteAttribute.cs | 9 +++ BackEnd/Timeline/Helpers/Cache/DataCacheHelper.cs | 4 +- BackEnd/Timeline/Services/Mapper/TimelineMapper.cs | 4 +- 21 files changed, 240 insertions(+), 152 deletions(-) delete mode 100644 BackEnd/Timeline/Controllers/ActionResultControllerExtensions.cs delete mode 100644 BackEnd/Timeline/Controllers/ControllerAuthExtensions.cs create mode 100644 BackEnd/Timeline/Controllers/MyControllerBase.cs create mode 100644 BackEnd/Timeline/Filters/NotEntityDeleteAttribute.cs (limited to 'BackEnd') diff --git a/BackEnd/Timeline.Tests/IntegratedTests/HttpClientTimelineExtensions.cs b/BackEnd/Timeline.Tests/IntegratedTests/HttpClientTimelineExtensions.cs index ac60ce7c..5ae4d8a2 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests/HttpClientTimelineExtensions.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests/HttpClientTimelineExtensions.cs @@ -15,7 +15,7 @@ namespace Timeline.Tests.IntegratedTests public static Task PutTimelineMemberAsync(this HttpClient client, string timelineName, string memberUsername) => client.TestPutAsync($"timelines/{timelineName}/members/{memberUsername}"); - public static Task DeleteTimelineMemberAsync(this HttpClient client, string timelineName, string memberUsername, bool? delete) - => client.TestDeleteAsync($"timelines/{timelineName}/members/{memberUsername}", delete); + public static Task DeleteTimelineMemberAsync(this HttpClient client, string timelineName, string memberUsername) + => client.TestDeleteAsync($"timelines/{timelineName}/members/{memberUsername}"); } } diff --git a/BackEnd/Timeline.Tests/IntegratedTests/TimelineTest.cs b/BackEnd/Timeline.Tests/IntegratedTests/TimelineTest.cs index 0e7bb735..e5fa310a 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests/TimelineTest.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests/TimelineTest.cs @@ -209,16 +209,16 @@ namespace Timeline.Tests.IntegratedTests using var client = await CreateClientAsAdministrator(); await client.TestDeleteAssertInvalidModelAsync("timelines/!!!"); - await client.TestDeleteAsync("timelines/t2"); - await client.TestDeleteAsync("timelines/t2"); + await client.TestDeleteAsync("timelines/t2", true); + await client.TestDeleteAsync("timelines/t2", false); } { using var client = await CreateClientAs(1); await client.TestDeleteAssertInvalidModelAsync("timelines/!!!"); - await client.TestDeleteAsync("timelines/t1"); - await client.TestDeleteAsync("timelines/t1"); + await client.TestDeleteAsync("timelines/t1", true); + await client.TestDeleteAssertForbiddenAsync("timelines/t1"); } } @@ -298,9 +298,9 @@ namespace Timeline.Tests.IntegratedTests await AssertEmptyMembers(); await client.PutTimelineMemberAsync(timelineName, "user2"); await AssertMembers(new List { await client.GetUserAsync("user2") }); - await client.DeleteTimelineMemberAsync(timelineName, "user2", true); + await client.DeleteTimelineMemberAsync(timelineName, "user2"); await AssertEmptyMembers(); - await client.TestDeleteAsync($"timelines/{timelineName}/members/usernotexist", false); + await client.TestDeleteAssertErrorAsync($"timelines/{timelineName}/members/usernotexist"); await AssertEmptyMembers(); } diff --git a/BackEnd/Timeline.Tests/IntegratedTests/UserPermissionTest.cs b/BackEnd/Timeline.Tests/IntegratedTests/UserPermissionTest.cs index bdb376de..a5877dde 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests/UserPermissionTest.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests/UserPermissionTest.cs @@ -190,7 +190,7 @@ namespace Timeline.Tests.IntegratedTests const string url = "users/user123/permissions/UserManagement"; await client.TestPutAssertErrorAsync(url, errorCode: ErrorCodes.NotExist.User); - await client.TestDeleteAsync(url); + await client.TestDeleteAssertErrorAsync(url); } } } diff --git a/BackEnd/Timeline/Auth/PrincipalExtensions.cs b/BackEnd/Timeline/Auth/PrincipalExtensions.cs index 6c974ed2..d338846a 100644 --- a/BackEnd/Timeline/Auth/PrincipalExtensions.cs +++ b/BackEnd/Timeline/Auth/PrincipalExtensions.cs @@ -6,7 +6,13 @@ namespace Timeline.Auth { public static class PrincipalExtensions { - public static long? GetUserId(this ClaimsPrincipal? principal) + public static string? GetOptionalName(this ClaimsPrincipal? principal) + { + if (principal is null) return null; + return principal.Identity?.Name; + } + + public static long? GetOptionalUserId(this ClaimsPrincipal? principal) { if (principal is null) return null; diff --git a/BackEnd/Timeline/Controllers/ActionResultControllerExtensions.cs b/BackEnd/Timeline/Controllers/ActionResultControllerExtensions.cs deleted file mode 100644 index a7a5486c..00000000 --- a/BackEnd/Timeline/Controllers/ActionResultControllerExtensions.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Timeline.Models.Http; - -namespace Timeline.Controllers -{ - public static class ActionResultControllerExtensions - { - public static ObjectResult StatusCodeWithCommonResponse(this ControllerBase controller, int statusCode, int code, string message) - { - return controller.StatusCode(statusCode, new CommonResponse(code, message)); - } - - public static ObjectResult ForbidWithMessage(this ControllerBase controller, string? message = null) - { - return controller.StatusCode(StatusCodes.Status403Forbidden, new CommonResponse(ErrorCodes.Common.Forbid, message ?? Resource.MessageForbid)); - } - - public static ObjectResult Delete(this ControllerBase controller, bool delete = true) - { - return controller.StatusCode(StatusCodes.Status200OK, CommonDeleteResponse.Create(delete)); - } - - public static BadRequestObjectResult BadRequestWithCommonResponse(this ControllerBase controller, int code, string message) - { - return controller.BadRequest(new CommonResponse(code, message)); - } - } -} diff --git a/BackEnd/Timeline/Controllers/BookmarkTimelineController.cs b/BackEnd/Timeline/Controllers/BookmarkTimelineController.cs index 94cb0f3e..bfbf82f3 100644 --- a/BackEnd/Timeline/Controllers/BookmarkTimelineController.cs +++ b/BackEnd/Timeline/Controllers/BookmarkTimelineController.cs @@ -16,7 +16,7 @@ namespace Timeline.Controllers /// [ApiController] [ProducesErrorResponseType(typeof(CommonResponse))] - public class BookmarkTimelineController : Controller + public class BookmarkTimelineController : MyControllerBase { private readonly IBookmarkTimelineService _service; private readonly ITimelineService _timelineService; @@ -44,7 +44,7 @@ namespace Timeline.Controllers [ProducesResponseType(401)] public async Task>> List() { - var ids = await _service.GetBookmarksAsync(this.GetUserId()); + var ids = await _service.GetBookmarksAsync(GetUserId()); var timelines = await _timelineService.GetTimelineList(ids); return await Map(timelines); } @@ -61,7 +61,7 @@ namespace Timeline.Controllers public async Task> Put([GeneralTimelineName] string timeline) { var timelineId = await _timelineService.GetTimelineIdByNameAsync(timeline); - var create = await _service.AddBookmarkAsync(this.GetUserId(), timelineId); + var create = await _service.AddBookmarkAsync(GetUserId(), timelineId); return CommonPutResponse.Create(create); } @@ -77,7 +77,7 @@ namespace Timeline.Controllers public async Task> Delete([GeneralTimelineName] string timeline) { var timelineId = await _timelineService.GetTimelineIdByNameAsync(timeline); - var delete = await _service.RemoveBookmarkAsync(this.GetUserId(), timelineId); + var delete = await _service.RemoveBookmarkAsync(GetUserId(), timelineId); return CommonDeleteResponse.Create(delete); } @@ -95,7 +95,7 @@ namespace Timeline.Controllers try { var timelineId = await _timelineService.GetTimelineIdByNameAsync(request.Timeline); - await _service.MoveBookmarkAsync(this.GetUserId(), timelineId, request.NewPosition!.Value); + await _service.MoveBookmarkAsync(GetUserId(), timelineId, request.NewPosition!.Value); return Ok(); } catch (InvalidBookmarkException) diff --git a/BackEnd/Timeline/Controllers/ControllerAuthExtensions.cs b/BackEnd/Timeline/Controllers/ControllerAuthExtensions.cs deleted file mode 100644 index cd2bdadf..00000000 --- a/BackEnd/Timeline/Controllers/ControllerAuthExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using System; -using Timeline.Auth; -using Timeline.Services.User; - -namespace Timeline.Controllers -{ - public static class ControllerAuthExtensions - { - public static bool UserHasPermission(this ControllerBase controller, UserPermission permission) - { - return controller.User.HasPermission(permission); - } - - public static long GetUserId(this ControllerBase controller) - { - return controller.GetOptionalUserId() ?? throw new InvalidOperationException(Resource.ExceptionNoUserId); - } - - public static long? GetOptionalUserId(this ControllerBase controller) - { - return controller.User.GetUserId(); - } - } -} diff --git a/BackEnd/Timeline/Controllers/HighlightTimelineController.cs b/BackEnd/Timeline/Controllers/HighlightTimelineController.cs index e73bc7a9..24201126 100644 --- a/BackEnd/Timeline/Controllers/HighlightTimelineController.cs +++ b/BackEnd/Timeline/Controllers/HighlightTimelineController.cs @@ -17,7 +17,7 @@ namespace Timeline.Controllers /// [ApiController] [ProducesErrorResponseType(typeof(CommonResponse))] - public class HighlightTimelineController : Controller + public class HighlightTimelineController : MyControllerBase { private readonly IHighlightTimelineService _service; private readonly ITimelineService _timelineService; @@ -61,7 +61,7 @@ namespace Timeline.Controllers public async Task> Put([GeneralTimelineName] string timeline) { var timelineId = await _timelineService.GetTimelineIdByNameAsync(timeline); - var create = await _service.AddHighlightTimelineAsync(timelineId, this.GetUserId()); + var create = await _service.AddHighlightTimelineAsync(timelineId, GetUserId()); return CommonPutResponse.Create(create); } @@ -78,7 +78,7 @@ namespace Timeline.Controllers public async Task> Delete([GeneralTimelineName] string timeline) { var timelineId = await _timelineService.GetTimelineIdByNameAsync(timeline); - var delete = await _service.RemoveHighlightTimelineAsync(timelineId, this.GetUserId()); + var delete = await _service.RemoveHighlightTimelineAsync(timelineId, GetUserId()); return CommonDeleteResponse.Create(delete); } diff --git a/BackEnd/Timeline/Controllers/MyControllerBase.cs b/BackEnd/Timeline/Controllers/MyControllerBase.cs new file mode 100644 index 00000000..d4ee9d3e --- /dev/null +++ b/BackEnd/Timeline/Controllers/MyControllerBase.cs @@ -0,0 +1,76 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System; +using Timeline.Auth; +using Timeline.Models.Http; +using Timeline.Services.User; + +namespace Timeline.Controllers +{ + public class MyControllerBase : ControllerBase + { + #region auth + protected bool UserHasPermission(UserPermission permission) + { + return User.HasPermission(permission); + } + + protected string? GetOptionalUsername() + { + return User.GetOptionalName(); + } + + protected string GetUsername() + { + return GetOptionalUsername() ?? throw new InvalidOperationException(Resource.ExceptionNoUsername); + } + + protected long? GetOptionalUserId() + { + return User.GetOptionalUserId(); + } + + protected long GetUserId() + { + return GetOptionalUserId() ?? throw new InvalidOperationException(Resource.ExceptionNoUserId); + } + #endregion auth + + #region action result + protected ObjectResult StatusCodeWithCommonResponse(int statusCode, int code, string message) + { + return StatusCode(statusCode, new CommonResponse(code, message)); + } + + protected ObjectResult OkWithCommonResponse(int statusCode = 0, string? message = null) + { + return Ok(new CommonResponse(statusCode, message ?? Resource.MessageOperationSucceeded)); + } + + protected ObjectResult OkWithCommonResponse(string? message) + { + return OkWithCommonResponse(message: message); + } + + protected ObjectResult ForbidWithCommonResponse(string? message = null) + { + return StatusCode(StatusCodes.Status403Forbidden, new CommonResponse(ErrorCodes.Common.Forbid, message ?? Resource.MessageForbid)); + } + + protected ObjectResult ForbidWithCommonResponse(int code, string? message = null) + { + return StatusCode(StatusCodes.Status403Forbidden, new CommonResponse(code, message ?? Resource.MessageForbid)); + } + + protected ObjectResult DeleteWithCommonDeleteResponse(bool delete = true) + { + return StatusCode(StatusCodes.Status200OK, CommonDeleteResponse.Create(delete)); + } + + protected BadRequestObjectResult BadRequestWithCommonResponse(int code, string message) + { + return BadRequest(new CommonResponse(code, message)); + } + #endregion action result + } +} diff --git a/BackEnd/Timeline/Controllers/Resource.Designer.cs b/BackEnd/Timeline/Controllers/Resource.Designer.cs index f3d7264a..c062380b 100644 --- a/BackEnd/Timeline/Controllers/Resource.Designer.cs +++ b/BackEnd/Timeline/Controllers/Resource.Designer.cs @@ -69,6 +69,15 @@ namespace Timeline.Controllers { } } + /// + /// Looks up a localized string similar to Can't get username.. + /// + internal static string ExceptionNoUsername { + get { + return ResourceManager.GetString("ExceptionNoUsername", resourceCulture); + } + } + /// /// Looks up a localized string similar to You have no permission to access this.. /// @@ -150,6 +159,15 @@ namespace Timeline.Controllers { } } + /// + /// Looks up a localized string similar to Operation succeeded.. + /// + internal static string MessageOperationSucceeded { + get { + return ResourceManager.GetString("MessageOperationSucceeded", resourceCulture); + } + } + /// /// Looks up a localized string similar to The user specified by query param "relate" does not exist.. /// diff --git a/BackEnd/Timeline/Controllers/Resource.resx b/BackEnd/Timeline/Controllers/Resource.resx index 90c6bdd6..afaa1ba1 100644 --- a/BackEnd/Timeline/Controllers/Resource.resx +++ b/BackEnd/Timeline/Controllers/Resource.resx @@ -120,6 +120,9 @@ Can't get user id. + + Can't get username. + You have no permission to access this. @@ -147,6 +150,9 @@ The old password is wrong. + + Operation succeeded. + The user specified by query param "relate" does not exist. diff --git a/BackEnd/Timeline/Controllers/SearchController.cs b/BackEnd/Timeline/Controllers/SearchController.cs index cd085e5b..358c3739 100644 --- a/BackEnd/Timeline/Controllers/SearchController.cs +++ b/BackEnd/Timeline/Controllers/SearchController.cs @@ -16,7 +16,7 @@ namespace Timeline.Controllers [ApiController] [ProducesErrorResponseType(typeof(CommonResponse))] [Route("search")] - public class SearchController : Controller + public class SearchController : MyControllerBase { private readonly ISearchService _service; private readonly IGenericMapper _mapper; diff --git a/BackEnd/Timeline/Controllers/TimelineController.cs b/BackEnd/Timeline/Controllers/TimelineController.cs index 7347f135..f98ff3e0 100644 --- a/BackEnd/Timeline/Controllers/TimelineController.cs +++ b/BackEnd/Timeline/Controllers/TimelineController.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Timeline.Entities; +using Timeline.Filters; using Timeline.Models; using Timeline.Models.Http; using Timeline.Models.Validation; @@ -21,7 +22,7 @@ namespace Timeline.Controllers [ApiController] [Route("timelines")] [ProducesErrorResponseType(typeof(CommonResponse))] - public class TimelineController : Controller + public class TimelineController : MyControllerBase { private readonly IUserService _userService; private readonly ITimelineService _service; @@ -34,7 +35,7 @@ namespace Timeline.Controllers _mapper = mapper; } - private bool UserHasAllTimelineManagementPermission => this.UserHasPermission(UserPermission.AllTimelineManagement); + private bool UserHasAllTimelineManagementPermission => UserHasPermission(UserPermission.AllTimelineManagement); private Task Map(TimelineEntity timeline) { @@ -82,7 +83,7 @@ namespace Timeline.Controllers } else { - return this.BadRequestWithCommonResponse(ErrorCodes.Common.InvalidModel, string.Format(Resource.MessageTimelineListQueryVisibilityUnknown, visibility)); + return BadRequestWithCommonResponse(ErrorCodes.Common.InvalidModel, string.Format(Resource.MessageTimelineListQueryVisibilityUnknown, visibility)); } } } @@ -100,7 +101,7 @@ namespace Timeline.Controllers } catch (EntityNotExistException) { - return this.BadRequestWithCommonResponse(ErrorCodes.TimelineController.QueryRelateNotExist, Resource.MessageTimelineListQueryRelateNotExist); + return BadRequestWithCommonResponse(ErrorCodes.TimelineController.QueryRelateNotExist, Resource.MessageTimelineListQueryRelateNotExist); } } @@ -141,9 +142,9 @@ namespace Timeline.Controllers { var timelineId = await _service.GetTimelineIdByNameAsync(timeline); - if (!UserHasAllTimelineManagementPermission && !await _service.HasManagePermissionAsync(timelineId, this.GetUserId())) + if (!UserHasAllTimelineManagementPermission && !await _service.HasManagePermissionAsync(timelineId, GetUserId())) { - return this.ForbidWithMessage(); + return ForbidWithCommonResponse(); } await _service.ChangePropertyAsync(timelineId, _mapper.AutoMapperMap(body)); @@ -167,14 +168,14 @@ namespace Timeline.Controllers { var timelineId = await _service.GetTimelineIdByNameAsync(timeline); - if (!UserHasAllTimelineManagementPermission && !(await _service.HasManagePermissionAsync(timelineId, this.GetUserId()))) + if (!UserHasAllTimelineManagementPermission && !(await _service.HasManagePermissionAsync(timelineId, GetUserId()))) { - return this.ForbidWithMessage(); + return ForbidWithCommonResponse(); } var userId = await _userService.GetUserIdByUsernameAsync(member); - var create = await _service.AddMemberAsync(timelineId, userId); - return Ok(CommonPutResponse.Create(create)); + await _service.AddMemberAsync(timelineId, userId); + return OkWithCommonResponse(); } /// @@ -184,6 +185,7 @@ namespace Timeline.Controllers /// The member's username. [HttpDelete("{timeline}/members/{member}")] [Authorize] + [NotEntityDelete] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] @@ -192,15 +194,14 @@ namespace Timeline.Controllers { var timelineId = await _service.GetTimelineIdByNameAsync(timeline); - if (!UserHasAllTimelineManagementPermission && !(await _service.HasManagePermissionAsync(timelineId, this.GetUserId()))) + if (!UserHasAllTimelineManagementPermission && !(await _service.HasManagePermissionAsync(timelineId, GetUserId()))) { - return this.ForbidWithMessage(); + return ForbidWithCommonResponse(); } - var userId = await _userService.GetUserIdByUsernameAsync(member); - var delete = await _service.RemoveMemberAsync(timelineId, userId); - return Ok(CommonDeleteResponse.Create(delete)); + await _service.RemoveMemberAsync(timelineId, userId); + return OkWithCommonResponse(); } /// @@ -215,7 +216,7 @@ namespace Timeline.Controllers [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task> TimelineCreate([FromBody] HttpTimelineCreateRequest body) { - var userId = this.GetUserId(); + var userId = GetUserId(); var timeline = await _service.CreateTimelineAsync(body.Name, userId); var result = await Map(timeline); @@ -235,15 +236,29 @@ namespace Timeline.Controllers [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task TimelineDelete([FromRoute][TimelineName] string timeline) { - var timelineId = await _service.GetTimelineIdByNameAsync(timeline); + try + { + var timelineId = await _service.GetTimelineIdByNameAsync(timeline); + + if (!UserHasAllTimelineManagementPermission && !(await _service.HasManagePermissionAsync(timelineId, GetUserId()))) + { + return ForbidWithCommonResponse(); + } - if (!UserHasAllTimelineManagementPermission && !(await _service.HasManagePermissionAsync(timelineId, this.GetUserId()))) + await _service.DeleteTimelineAsync(timelineId); + return DeleteWithCommonDeleteResponse(); + } + catch (EntityNotExistException) { - return this.ForbidWithMessage(); + if (UserHasAllTimelineManagementPermission) + { + return DeleteWithCommonDeleteResponse(false); + } + else + { + return ForbidWithCommonResponse(); + } } - - await _service.DeleteTimelineAsync(timelineId); - return this.Delete(); } } } diff --git a/BackEnd/Timeline/Controllers/TimelinePostController.cs b/BackEnd/Timeline/Controllers/TimelinePostController.cs index 09e7e624..c33d47aa 100644 --- a/BackEnd/Timeline/Controllers/TimelinePostController.cs +++ b/BackEnd/Timeline/Controllers/TimelinePostController.cs @@ -22,7 +22,7 @@ namespace Timeline.Controllers [ApiController] [Route("timelines/{timeline}/posts")] [ProducesErrorResponseType(typeof(CommonResponse))] - public class TimelinePostController : Controller + public class TimelinePostController : MyControllerBase { private readonly ITimelineService _timelineService; private readonly ITimelinePostService _postService; @@ -39,7 +39,7 @@ namespace Timeline.Controllers _markdownProcessor = markdownProcessor; } - private bool UserHasAllTimelineManagementPermission => this.UserHasPermission(UserPermission.AllTimelineManagement); + private bool UserHasAllTimelineManagementPermission => UserHasPermission(UserPermission.AllTimelineManagement); private Task Map(TimelinePostEntity post) { @@ -66,9 +66,9 @@ namespace Timeline.Controllers { var timelineId = await _timelineService.GetTimelineIdByNameAsync(timeline); - if (!UserHasAllTimelineManagementPermission && !await _timelineService.HasReadPermissionAsync(timelineId, this.GetOptionalUserId())) + if (!UserHasAllTimelineManagementPermission && !await _timelineService.HasReadPermissionAsync(timelineId, GetOptionalUserId())) { - return this.ForbidWithMessage(); + return ForbidWithCommonResponse(); } var posts = await _postService.GetPostsAsync(timelineId, modifiedSince, includeDeleted ?? false); @@ -91,9 +91,9 @@ namespace Timeline.Controllers { var timelineId = await _timelineService.GetTimelineIdByNameAsync(timeline); - if (!UserHasAllTimelineManagementPermission && !await _timelineService.HasReadPermissionAsync(timelineId, this.GetOptionalUserId())) + if (!UserHasAllTimelineManagementPermission && !await _timelineService.HasReadPermissionAsync(timelineId, GetOptionalUserId())) { - return this.ForbidWithMessage(); + return ForbidWithCommonResponse(); } var post = await _postService.GetPostAsync(timelineId, postId); @@ -137,9 +137,9 @@ namespace Timeline.Controllers { var timelineId = await _timelineService.GetTimelineIdByNameAsync(timeline); - if (!UserHasAllTimelineManagementPermission && !await _timelineService.HasReadPermissionAsync(timelineId, this.GetOptionalUserId())) + if (!UserHasAllTimelineManagementPermission && !await _timelineService.HasReadPermissionAsync(timelineId, GetOptionalUserId())) { - return this.ForbidWithMessage(); + return ForbidWithCommonResponse(); } return await DataCacheHelper.GenerateActionResult(this, @@ -171,11 +171,11 @@ namespace Timeline.Controllers public async Task> Post([FromRoute][GeneralTimelineName] string timeline, [FromBody] HttpTimelinePostCreateRequest body) { var timelineId = await _timelineService.GetTimelineIdByNameAsync(timeline); - var userId = this.GetUserId(); + var userId = GetUserId(); if (!UserHasAllTimelineManagementPermission && !await _timelineService.IsMemberOfAsync(timelineId, userId)) { - return this.ForbidWithMessage(); + return ForbidWithCommonResponse(); } var createRequest = new TimelinePostCreateRequest() @@ -232,9 +232,9 @@ namespace Timeline.Controllers { var timelineId = await _timelineService.GetTimelineIdByNameAsync(timeline); - if (!UserHasAllTimelineManagementPermission && !await _postService.HasPostModifyPermissionAsync(timelineId, post, this.GetUserId(), true)) + if (!UserHasAllTimelineManagementPermission && !await _postService.HasPostModifyPermissionAsync(timelineId, post, GetUserId(), true)) { - return this.ForbidWithMessage(); + return ForbidWithCommonResponse(); } var entity = await _postService.PatchPostAsync(timelineId, post, new TimelinePostPatchRequest { Time = body.Time, Color = body.Color }); @@ -259,14 +259,14 @@ namespace Timeline.Controllers { var timelineId = await _timelineService.GetTimelineIdByNameAsync(timeline); - if (!UserHasAllTimelineManagementPermission && !await _postService.HasPostModifyPermissionAsync(timelineId, post, this.GetUserId(), true)) + if (!UserHasAllTimelineManagementPermission && !await _postService.HasPostModifyPermissionAsync(timelineId, post, GetUserId(), true)) { - return this.ForbidWithMessage(); + return ForbidWithCommonResponse(); } await _postService.DeletePostAsync(timelineId, post); - return this.Delete(); + return DeleteWithCommonDeleteResponse(); } } } diff --git a/BackEnd/Timeline/Controllers/TokenController.cs b/BackEnd/Timeline/Controllers/TokenController.cs index 080a4dc4..ae3e1b94 100644 --- a/BackEnd/Timeline/Controllers/TokenController.cs +++ b/BackEnd/Timeline/Controllers/TokenController.cs @@ -17,7 +17,7 @@ namespace Timeline.Controllers [Route("token")] [ApiController] [ProducesErrorResponseType(typeof(CommonResponse))] - public class TokenController : Controller + public class TokenController : MyControllerBase { private readonly IUserTokenManager _userTokenManager; private readonly IGenericMapper _mapper; @@ -57,11 +57,11 @@ namespace Timeline.Controllers } catch (EntityNotExistException) { - return this.BadRequestWithCommonResponse(ErrorCodes.TokenController.CreateBadCredential, Resource.MessageTokenCreateBadCredential); + return BadRequestWithCommonResponse(ErrorCodes.TokenController.CreateBadCredential, Resource.MessageTokenCreateBadCredential); } catch (BadPasswordException) { - return this.BadRequestWithCommonResponse(ErrorCodes.TokenController.CreateBadCredential, Resource.MessageTokenCreateBadCredential); + return BadRequestWithCommonResponse(ErrorCodes.TokenController.CreateBadCredential, Resource.MessageTokenCreateBadCredential); } } @@ -85,19 +85,19 @@ namespace Timeline.Controllers } catch (UserTokenTimeExpiredException) { - return this.BadRequestWithCommonResponse(ErrorCodes.TokenController.VerifyTimeExpired, Resource.MessageTokenVerifyTimeExpired); + return BadRequestWithCommonResponse(ErrorCodes.TokenController.VerifyTimeExpired, Resource.MessageTokenVerifyTimeExpired); } catch (UserTokenVersionExpiredException) { - return this.BadRequestWithCommonResponse(ErrorCodes.TokenController.VerifyOldVersion, Resource.MessageTokenVerifyOldVersion); + return BadRequestWithCommonResponse(ErrorCodes.TokenController.VerifyOldVersion, Resource.MessageTokenVerifyOldVersion); } catch (UserTokenBadFormatException) { - return this.BadRequestWithCommonResponse(ErrorCodes.TokenController.VerifyBadFormat, Resource.MessageTokenVerifyBadFormat); + return BadRequestWithCommonResponse(ErrorCodes.TokenController.VerifyBadFormat, Resource.MessageTokenVerifyBadFormat); } catch (UserTokenUserNotExistException) { - return this.BadRequestWithCommonResponse(ErrorCodes.TokenController.VerifyUserNotExist, Resource.MessageTokenVerifyUserNotExist); + return BadRequestWithCommonResponse(ErrorCodes.TokenController.VerifyUserNotExist, Resource.MessageTokenVerifyUserNotExist); } } } diff --git a/BackEnd/Timeline/Controllers/UserAvatarController.cs b/BackEnd/Timeline/Controllers/UserAvatarController.cs index 47d46a54..376e1f11 100644 --- a/BackEnd/Timeline/Controllers/UserAvatarController.cs +++ b/BackEnd/Timeline/Controllers/UserAvatarController.cs @@ -18,7 +18,7 @@ namespace Timeline.Controllers /// [ApiController] [ProducesErrorResponseType(typeof(CommonResponse))] - public class UserAvatarController : Controller + public class UserAvatarController : MyControllerBase { private readonly IUserService _userService; private readonly IUserAvatarService _service; @@ -62,9 +62,9 @@ namespace Timeline.Controllers [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task Put([FromRoute][Username] string username, [FromBody] ByteData body) { - if (!this.UserHasPermission(UserPermission.UserManagement) && User.Identity!.Name != username) + if (!UserHasPermission(UserPermission.UserManagement) && User.Identity!.Name != username) { - return this.ForbidWithMessage(Resource.MessageForbidNotAdministratorOrOwner); + return ForbidWithCommonResponse(Resource.MessageForbidNotAdministratorOrOwner); } long id = await _userService.GetUserIdByUsernameAsync(username); @@ -105,9 +105,9 @@ namespace Timeline.Controllers [Authorize] public async Task Delete([FromRoute][Username] string username) { - if (!this.UserHasPermission(UserPermission.UserManagement) && User.Identity!.Name != username) + if (!UserHasPermission(UserPermission.UserManagement) && User.Identity!.Name != username) { - return this.ForbidWithMessage(Resource.MessageForbidNotAdministratorOrOwner); + return ForbidWithCommonResponse(Resource.MessageForbidNotAdministratorOrOwner); } long id = await _userService.GetUserIdByUsernameAsync(username); diff --git a/BackEnd/Timeline/Controllers/UserController.cs b/BackEnd/Timeline/Controllers/UserController.cs index 47d2ee41..740bd0ed 100644 --- a/BackEnd/Timeline/Controllers/UserController.cs +++ b/BackEnd/Timeline/Controllers/UserController.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using System.Threading.Tasks; using Timeline.Auth; +using Timeline.Filters; using Timeline.Models.Http; using Timeline.Models.Validation; using Timeline.Services.Mapper; @@ -16,7 +17,7 @@ namespace Timeline.Controllers /// [ApiController] [ProducesErrorResponseType(typeof(CommonResponse))] - public class UserController : Controller + public class UserController : MyControllerBase { private readonly IUserService _userService; private readonly IUserPermissionService _userPermissionService; @@ -32,7 +33,7 @@ namespace Timeline.Controllers _mapper = mapper; } - private bool UserHasUserManagementPermission => this.UserHasPermission(UserPermission.UserManagement); + private bool UserHasUserManagementPermission => UserHasPermission(UserPermission.UserManagement); /// /// Get all users. @@ -51,14 +52,14 @@ namespace Timeline.Controllers /// Create a new user. You have to be administrator. /// /// The new user's info. - [HttpPost("users"), PermissionAuthorize(UserPermission.UserManagement)] + [HttpPost("users")] + [PermissionAuthorize(UserPermission.UserManagement)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task> Post([FromBody] HttpUserPostRequest body) { - var user = await _userService.CreateUserAsync( new CreateUserParams(body.Username, body.Password) { Nickname = body.Nickname }); return await _mapper.MapAsync(user, Url, User); @@ -85,7 +86,8 @@ namespace Timeline.Controllers /// /// Username of the user to change. /// The new user info. - [HttpPatch("users/{username}"), Authorize] + [HttpPatch("users/{username}")] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] @@ -101,16 +103,16 @@ namespace Timeline.Controllers } else { - if (User.Identity!.Name != username) - return this.ForbidWithMessage(Resource.MessageForbidNotAdministratorOrOwner); + if (GetUsername() != username) + return ForbidWithCommonResponse(Resource.MessageForbidNotAdministratorOrOwner); - if (body.Username != null) - return this.ForbidWithMessage(Resource.MessageForbidNotAdministrator); + if (body.Username is not null) + return ForbidWithCommonResponse(Resource.MessageForbidNotAdministrator); - if (body.Password != null) - return this.ForbidWithMessage(Resource.MessageForbidNotAdministrator); + if (body.Password is not null) + return ForbidWithCommonResponse(Resource.MessageForbidNotAdministrator); - var user = await _userService.ModifyUserAsync(this.GetUserId(), _mapper.AutoMapperMap(body)); + var user = await _userService.ModifyUserAsync(GetUserId(), _mapper.AutoMapperMap(body)); return await _mapper.MapAsync(user, Url, User); } } @@ -120,7 +122,8 @@ namespace Timeline.Controllers /// /// Username of the user to delete. /// Info of deletion. - [HttpDelete("users/{username}"), PermissionAuthorize(UserPermission.UserManagement)] + [HttpDelete("users/{username}")] + [PermissionAuthorize(UserPermission.UserManagement)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] @@ -130,11 +133,11 @@ namespace Timeline.Controllers try { await _userDeleteService.DeleteUserAsync(username); - return this.Delete(); + return DeleteWithCommonDeleteResponse(); } catch (InvalidOperationOnRootUserException) { - return this.BadRequestWithCommonResponse(ErrorCodes.UserController.InvalidOperationOnRootUser, Resource.MessageInvalidOperationOnRootUser); + return BadRequestWithCommonResponse(ErrorCodes.UserController.InvalidOperationOnRootUser, Resource.MessageInvalidOperationOnRootUser); } } @@ -145,16 +148,16 @@ namespace Timeline.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] - public async Task ChangePassword([FromBody] HttpChangePasswordRequest request) + public async Task> ChangePassword([FromBody] HttpChangePasswordRequest request) { try { - await _userService.ChangePassword(this.GetUserId(), request.OldPassword, request.NewPassword); - return Ok(); + await _userService.ChangePassword(GetUserId(), request.OldPassword, request.NewPassword); + return OkWithCommonResponse(); } catch (BadPasswordException) { - return this.BadRequestWithCommonResponse(ErrorCodes.UserController.ChangePasswordBadOldPassword, Resource.MessageOldPasswordWrong); + return BadRequestWithCommonResponse(ErrorCodes.UserController.ChangePasswordBadOldPassword, Resource.MessageOldPasswordWrong); } // User can't be non-existent or the token is bad. } @@ -165,36 +168,37 @@ namespace Timeline.Controllers [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task PutUserPermission([FromRoute][Username] string username, [FromRoute] UserPermission permission) + public async Task> PutUserPermission([FromRoute][Username] string username, [FromRoute] UserPermission permission) { try { var id = await _userService.GetUserIdByUsernameAsync(username); await _userPermissionService.AddPermissionToUserAsync(id, permission); - return Ok(); + return OkWithCommonResponse(); } catch (InvalidOperationOnRootUserException) { - return this.BadRequestWithCommonResponse(ErrorCodes.UserController.InvalidOperationOnRootUser, Resource.MessageInvalidOperationOnRootUser); + return BadRequestWithCommonResponse(ErrorCodes.UserController.InvalidOperationOnRootUser, Resource.MessageInvalidOperationOnRootUser); } } [HttpDelete("users/{username}/permissions/{permission}"), PermissionAuthorize(UserPermission.UserManagement)] + [NotEntityDelete] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - public async Task DeleteUserPermission([FromRoute][Username] string username, [FromRoute] UserPermission permission) + public async Task> DeleteUserPermission([FromRoute][Username] string username, [FromRoute] UserPermission permission) { try { var id = await _userService.GetUserIdByUsernameAsync(username); await _userPermissionService.RemovePermissionFromUserAsync(id, permission); - return Ok(); + return OkWithCommonResponse(); } catch (InvalidOperationOnRootUserException) { - return this.BadRequestWithCommonResponse(ErrorCodes.UserController.InvalidOperationOnRootUser, Resource.MessageInvalidOperationOnRootUser); + return BadRequestWithCommonResponse(ErrorCodes.UserController.InvalidOperationOnRootUser, Resource.MessageInvalidOperationOnRootUser); } } } diff --git a/BackEnd/Timeline/Filters/CatchEntityNotExistExceptionFilter.cs b/BackEnd/Timeline/Filters/CatchEntityNotExistExceptionFilter.cs index 225fab4f..d97ffe9f 100644 --- a/BackEnd/Timeline/Filters/CatchEntityNotExistExceptionFilter.cs +++ b/BackEnd/Timeline/Filters/CatchEntityNotExistExceptionFilter.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; +using System.Linq; using Timeline.Models.Http; using Timeline.Services; @@ -28,7 +29,14 @@ namespace Timeline.Filters } else if (HttpMethods.IsDelete(context.HttpContext.Request.Method)) { - context.Result = new OkObjectResult(CommonDeleteResponse.NotExist()); + if (context.ActionDescriptor.EndpointMetadata.OfType().Any()) + { + context.Result = new BadRequestObjectResult(MakeCommonResponse(e)); + } + else + { + context.Result = new OkObjectResult(CommonDeleteResponse.NotExist()); + } } else { diff --git a/BackEnd/Timeline/Filters/NotEntityDeleteAttribute.cs b/BackEnd/Timeline/Filters/NotEntityDeleteAttribute.cs new file mode 100644 index 00000000..a6d5dac7 --- /dev/null +++ b/BackEnd/Timeline/Filters/NotEntityDeleteAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Timeline.Filters +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public class NotEntityDeleteAttribute : Attribute + { + } +} diff --git a/BackEnd/Timeline/Helpers/Cache/DataCacheHelper.cs b/BackEnd/Timeline/Helpers/Cache/DataCacheHelper.cs index 1d1ab64e..79cac23b 100644 --- a/BackEnd/Timeline/Helpers/Cache/DataCacheHelper.cs +++ b/BackEnd/Timeline/Helpers/Cache/DataCacheHelper.cs @@ -11,7 +11,7 @@ namespace Timeline.Helpers.Cache { public static class DataCacheHelper { - public static async Task GenerateActionResult(Controller controller, ICacheableDataProvider provider, TimeSpan? maxAge = null) + public static async Task GenerateActionResult(ControllerBase controller, ICacheableDataProvider provider, TimeSpan? maxAge = null) { const string CacheControlHeaderKey = "Cache-Control"; const string IfNonMatchHeaderKey = "If-None-Match"; @@ -74,7 +74,7 @@ namespace Timeline.Helpers.Cache return controller.File(data.Data, data.ContentType, digest.LastModified, eTag); } - public static Task GenerateActionResult(Controller controller, Func> getDigestDelegate, Func> getDataDelegate, TimeSpan? maxAge = null) + public static Task GenerateActionResult(ControllerBase controller, Func> getDigestDelegate, Func> getDataDelegate, TimeSpan? maxAge = null) { return GenerateActionResult(controller, new DelegateCacheableDataProvider(getDigestDelegate, getDataDelegate), maxAge); } diff --git a/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs b/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs index d3159423..87bebbb3 100644 --- a/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs +++ b/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs @@ -41,7 +41,7 @@ namespace Timeline.Services.Mapper public async Task MapAsync(TimelineEntity entity, IUrlHelper urlHelper, ClaimsPrincipal? user) { - var userId = user.GetUserId(); + var userId = user.GetOptionalUserId(); await _database.Entry(entity).Reference(e => e.Owner).LoadAsync(); await _database.Entry(entity).Collection(e => e.Members).Query().Include(m => m.User).LoadAsync(); @@ -99,7 +99,7 @@ namespace Timeline.Services.Mapper public async Task MapAsync(TimelinePostEntity entity, IUrlHelper urlHelper, ClaimsPrincipal? user) { - var userId = user.GetUserId(); + var userId = user.GetOptionalUserId(); await _database.Entry(entity).Reference(e => e.Timeline).LoadAsync(); await _database.Entry(entity).Collection(p => p.DataList).LoadAsync(); -- cgit v1.2.3