diff options
Diffstat (limited to 'Timeline/Controllers')
-rw-r--r-- | Timeline/Controllers/ControllerAuthExtensions.cs | 15 | ||||
-rw-r--r-- | Timeline/Controllers/PersonalTimelineController.cs | 111 | ||||
-rw-r--r-- | Timeline/Controllers/Testing/TestingI18nController.cs | 36 | ||||
-rw-r--r-- | Timeline/Controllers/TokenController.cs | 18 | ||||
-rw-r--r-- | Timeline/Controllers/UserAvatarController.cs | 89 | ||||
-rw-r--r-- | Timeline/Controllers/UserController.cs | 58 |
6 files changed, 156 insertions, 171 deletions
diff --git a/Timeline/Controllers/ControllerAuthExtensions.cs b/Timeline/Controllers/ControllerAuthExtensions.cs index 81fd2428..90da8a93 100644 --- a/Timeline/Controllers/ControllerAuthExtensions.cs +++ b/Timeline/Controllers/ControllerAuthExtensions.cs @@ -26,5 +26,20 @@ namespace Timeline.Controllers throw new InvalidOperationException("Failed to get user id because NameIdentifier claim is not a number.");
}
+
+ public static long? GetOptionalUserId(this ControllerBase controller)
+ {
+ if (controller.User == null)
+ return null;
+
+ var claim = controller.User.FindFirst(ClaimTypes.NameIdentifier);
+ if (claim == null)
+ throw new InvalidOperationException("Failed to get user id because User has no NameIdentifier claim.");
+
+ if (long.TryParse(claim.Value, out var value))
+ return value;
+
+ throw new InvalidOperationException("Failed to get user id because NameIdentifier claim is not a number.");
+ }
}
}
diff --git a/Timeline/Controllers/PersonalTimelineController.cs b/Timeline/Controllers/PersonalTimelineController.cs index 2c70fad1..27618c41 100644 --- a/Timeline/Controllers/PersonalTimelineController.cs +++ b/Timeline/Controllers/PersonalTimelineController.cs @@ -4,45 +4,21 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;
-using Timeline.Auth;
using Timeline.Filters;
-using Timeline.Models;
using Timeline.Models.Http;
using Timeline.Models.Validation;
using Timeline.Services;
-using static Timeline.Resources.Controllers.TimelineController;
-using static Timeline.Resources.Messages;
namespace Timeline.Controllers
{
[ApiController]
+ [CatchTimelineNotExistException]
public class PersonalTimelineController : Controller
{
private readonly ILogger<PersonalTimelineController> _logger;
private readonly IPersonalTimelineService _service;
- private bool IsAdmin()
- {
- if (User != null)
- {
- return User.IsAdministrator();
- }
- return false;
- }
-
- private string? GetAuthUsername()
- {
- if (User == null)
- {
- return null;
- }
- else
- {
- return User.Identity.Name;
- }
- }
-
public PersonalTimelineController(ILogger<PersonalTimelineController> logger, IPersonalTimelineService service)
{
_logger = logger;
@@ -50,17 +26,15 @@ namespace Timeline.Controllers }
[HttpGet("users/{username}/timeline")]
- [CatchTimelineNotExistException]
public async Task<ActionResult<BaseTimelineInfo>> TimelineGet([FromRoute][Username] string username)
{
return await _service.GetTimeline(username);
}
[HttpGet("users/{username}/timeline/posts")]
- [CatchTimelineNotExistException]
public async Task<ActionResult<IList<TimelinePostInfo>>> PostListGet([FromRoute][Username] string username)
{
- if (!IsAdmin() && !await _service.HasReadPermission(username, GetAuthUsername()))
+ if (!this.IsAdministrator() && !await _service.HasReadPermission(username, this.GetOptionalUserId()))
{
return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
@@ -68,77 +42,88 @@ namespace Timeline.Controllers return await _service.GetPosts(username);
}
- [HttpPost("users/{username}/timeline/postop/create")]
+ [HttpPost("users/{username}/timeline/posts")]
[Authorize]
- [CatchTimelineNotExistException]
- public async Task<ActionResult<TimelinePostCreateResponse>> PostOperationCreate([FromRoute][Username] string username, [FromBody] TimelinePostCreateRequest body)
+ public async Task<ActionResult<TimelinePostInfo>> PostPost([FromRoute][Username] string username, [FromBody] TimelinePostCreateRequest body)
{
- if (!IsAdmin() && !await _service.IsMemberOf(username, GetAuthUsername()!))
+ var id = this.GetUserId();
+ if (!this.IsAdministrator() && !await _service.IsMemberOf(username, id))
{
return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
- var res = await _service.CreatePost(username, User.Identity.Name!, body.Content, body.Time);
+ var res = await _service.CreatePost(username, id, body.Content, body.Time);
return res;
}
- [HttpPost("users/{username}/timeline/postop/delete")]
+ [HttpDelete("users/{username}/timeline/posts/{id}")]
[Authorize]
- [CatchTimelineNotExistException]
- public async Task<ActionResult> PostOperationDelete([FromRoute][Username] string username, [FromBody] TimelinePostDeleteRequest body)
+ public async Task<ActionResult> PostDelete([FromRoute][Username] string username, [FromRoute] long id)
{
try
{
- var postId = body.Id!.Value;
- if (!IsAdmin() && !await _service.HasPostModifyPermission(username, postId, GetAuthUsername()!))
+ if (!this.IsAdministrator() && !await _service.HasPostModifyPermission(username, id, this.GetUserId()))
{
return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
- await _service.DeletePost(username, postId);
+ await _service.DeletePost(username, id);
+ return Ok(CommonDeleteResponse.Delete());
}
catch (TimelinePostNotExistException)
{
- return BadRequest(ErrorResponse.TimelineController.PostOperationDelete_NotExist());
+ return Ok(CommonDeleteResponse.NotExist());
}
- return Ok();
}
- [HttpPost("users/{username}/timeline/op/property")]
+ [HttpPatch("users/{username}/timeline")]
[Authorize]
- [SelfOrAdmin]
- [CatchTimelineNotExistException]
- public async Task<ActionResult> TimelineChangeProperty([FromRoute][Username] string username, [FromBody] TimelinePropertyChangeRequest body)
+ public async Task<ActionResult> TimelinePatch([FromRoute][Username] string username, [FromBody] TimelinePatchRequest body)
{
+ if (!this.IsAdministrator() && !(User.Identity.Name == username))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
await _service.ChangeProperty(username, body);
return Ok();
}
- [HttpPost("users/{username}/timeline/op/member")]
+ [HttpPut("users/{username}/timeline/members/{member}")]
[Authorize]
- [SelfOrAdmin]
- [CatchTimelineNotExistException]
- public async Task<ActionResult> TimelineChangeMember([FromRoute][Username] string username, [FromBody] TimelineMemberChangeRequest body)
+ public async Task<ActionResult> TimelineMemberPut([FromRoute][Username] string username, [FromRoute][Username] string member)
{
+ if (!this.IsAdministrator() && !(User.Identity.Name == username))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
+
try
{
- await _service.ChangeMember(username, body.Add, body.Remove);
+ await _service.ChangeMember(username, new List<string> { member }, null);
return Ok();
}
- catch (TimelineMemberOperationUserException e)
+ catch (UserNotExistException)
{
- if (e.InnerException is UsernameBadFormatException)
- {
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(
- TimelineController_ChangeMember_UsernameBadFormat, e.Index, e.Operation));
- }
- else if (e.InnerException is UserNotExistException)
- {
- return BadRequest(ErrorResponse.UserCommon.CustomMessage_NotExist(
- TimelineController_ChangeMember_UserNotExist, e.Index, e.Operation));
- }
+ return BadRequest(ErrorResponse.TimelineController.MemberPut_NotExist());
+ }
+ }
+
+ [HttpDelete("users/{username}/timeline/members/{member}")]
+ [Authorize]
+ public async Task<ActionResult> TimelineMemberDelete([FromRoute][Username] string username, [FromRoute][Username] string member)
+ {
+ if (!this.IsAdministrator() && !(User.Identity.Name == username))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
- _logger.LogError(e, LogUnknownTimelineMemberOperationUserException);
- throw;
+ try
+ {
+ await _service.ChangeMember(username, null, new List<string> { member });
+ return Ok(CommonDeleteResponse.Delete());
+ }
+ catch (UserNotExistException)
+ {
+ return Ok(CommonDeleteResponse.NotExist());
}
}
}
diff --git a/Timeline/Controllers/Testing/TestingI18nController.cs b/Timeline/Controllers/Testing/TestingI18nController.cs deleted file mode 100644 index febb56a5..00000000 --- a/Timeline/Controllers/Testing/TestingI18nController.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Localization;
-
-// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
-
-namespace Timeline.Controllers.Testing
-{
- [Route("testing/i18n")]
- [ApiController]
- public class TestingI18nController : Controller
- {
- private readonly IStringLocalizer<TestingI18nController> _stringLocalizer;
-
- public TestingI18nController(IStringLocalizer<TestingI18nController> stringLocalizer)
- {
- _stringLocalizer = stringLocalizer;
- }
-
- [HttpGet("direct")]
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static")]
- public ActionResult<string> Direct()
- {
- return Resources.Controllers.Testing.TestingI18nController.TestString;
- }
-
- [HttpGet("localizer")]
- public ActionResult<string> Localizer()
- {
- return _stringLocalizer["TestString"].Value;
- }
- }
-}
diff --git a/Timeline/Controllers/TokenController.cs b/Timeline/Controllers/TokenController.cs index 9724c1a6..a7f5fbde 100644 --- a/Timeline/Controllers/TokenController.cs +++ b/Timeline/Controllers/TokenController.cs @@ -1,3 +1,4 @@ +using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
@@ -5,7 +6,6 @@ using System; using System.Globalization;
using System.Threading.Tasks;
using Timeline.Helpers;
-using Timeline.Models;
using Timeline.Models.Http;
using Timeline.Services;
using static Timeline.Resources.Controllers.TokenController;
@@ -20,20 +20,14 @@ namespace Timeline.Controllers private readonly ILogger<TokenController> _logger;
private readonly IClock _clock;
- private static Models.Http.User CreateUserFromUserInfo(Models.User userInfo)
- {
- return new Models.Http.User
- {
- Username = userInfo.Username,
- Administrator = userInfo.Administrator
- };
- }
+ private readonly IMapper _mapper;
- public TokenController(IUserTokenManager userTokenManager, ILogger<TokenController> logger, IClock clock)
+ public TokenController(IUserTokenManager userTokenManager, ILogger<TokenController> logger, IClock clock, IMapper mapper)
{
_userTokenManager = userTokenManager;
_logger = logger;
_clock = clock;
+ _mapper = mapper;
}
[HttpPost("create")]
@@ -65,7 +59,7 @@ namespace Timeline.Controllers return Ok(new CreateTokenResponse
{
Token = result.Token,
- User = CreateUserFromUserInfo(result.User)
+ User = _mapper.Map<UserInfoForAdmin>(result.User)
});
}
catch (UserNotExistException e)
@@ -100,7 +94,7 @@ namespace Timeline.Controllers ("Username", result.Username), ("Token", request.Token)));
return Ok(new VerifyTokenResponse
{
- User = CreateUserFromUserInfo(result)
+ User = _mapper.Map<UserInfoForAdmin>(result)
});
}
catch (UserTokenTimeExpireException e)
diff --git a/Timeline/Controllers/UserAvatarController.cs b/Timeline/Controllers/UserAvatarController.cs index 62f1d78c..ab0ad8e7 100644 --- a/Timeline/Controllers/UserAvatarController.cs +++ b/Timeline/Controllers/UserAvatarController.cs @@ -21,11 +21,13 @@ namespace Timeline.Controllers {
private readonly ILogger<UserAvatarController> _logger;
+ private readonly IUserService _userService;
private readonly IUserAvatarService _service;
- public UserAvatarController(ILogger<UserAvatarController> logger, IUserAvatarService service)
+ public UserAvatarController(ILogger<UserAvatarController> logger, IUserService userService, IUserAvatarService service)
{
_logger = logger;
+ _userService = userService;
_service = service;
}
@@ -33,46 +35,50 @@ namespace Timeline.Controllers [ResponseCache(NoStore = false, Location = ResponseCacheLocation.None, Duration = 0)]
public async Task<IActionResult> Get([FromRoute][Username] string username)
{
- const string IfNonMatchHeaderKey = "If-None-Match";
-
+ long id;
try
{
- var eTagValue = $"\"{await _service.GetAvatarETag(username)}\"";
- var eTag = new EntityTagHeaderValue(eTagValue);
-
- if (Request.Headers.TryGetValue(IfNonMatchHeaderKey, out var value))
- {
- if (!EntityTagHeaderValue.TryParseStrictList(value, out var eTagList))
- {
- _logger.LogInformation(Log.Format(LogGetBadIfNoneMatch,
- ("Username", username), ("If-None-Match", value)));
- return BadRequest(ErrorResponse.Common.Header.IfNonMatch_BadFormat());
- }
-
- if (eTagList.FirstOrDefault(e => e.Equals(eTag)) != null)
- {
- Response.Headers.Add("ETag", eTagValue);
- _logger.LogInformation(Log.Format(LogGetReturnNotModify, ("Username", username)));
- return StatusCode(StatusCodes.Status304NotModified);
- }
- }
-
- var avatarInfo = await _service.GetAvatar(username);
- var avatar = avatarInfo.Avatar;
-
- _logger.LogInformation(Log.Format(LogGetReturnData, ("Username", username)));
- return File(avatar.Data, avatar.Type, new DateTimeOffset(avatarInfo.LastModified), eTag);
+ id = await _userService.GetUserIdByUsername(username);
}
catch (UserNotExistException e)
{
_logger.LogInformation(e, Log.Format(LogGetUserNotExist, ("Username", username)));
return NotFound(ErrorResponse.UserCommon.NotExist());
}
+
+ const string IfNonMatchHeaderKey = "If-None-Match";
+
+ var eTagValue = $"\"{await _service.GetAvatarETag(id)}\"";
+ var eTag = new EntityTagHeaderValue(eTagValue);
+
+ if (Request.Headers.TryGetValue(IfNonMatchHeaderKey, out var value))
+ {
+ if (!EntityTagHeaderValue.TryParseStrictList(value, out var eTagList))
+ {
+ _logger.LogInformation(Log.Format(LogGetBadIfNoneMatch,
+ ("Username", username), ("If-None-Match", value)));
+ return BadRequest(ErrorResponse.Common.Header.IfNonMatch_BadFormat());
+ }
+
+ if (eTagList.FirstOrDefault(e => e.Equals(eTag)) != null)
+ {
+ Response.Headers.Add("ETag", eTagValue);
+ _logger.LogInformation(Log.Format(LogGetReturnNotModify, ("Username", username)));
+ return StatusCode(StatusCodes.Status304NotModified);
+ }
+ }
+
+ var avatarInfo = await _service.GetAvatar(id);
+ var avatar = avatarInfo.Avatar;
+
+ _logger.LogInformation(Log.Format(LogGetReturnData, ("Username", username)));
+ return File(avatar.Data, avatar.Type, new DateTimeOffset(avatarInfo.LastModified), eTag);
+
}
[HttpPut("users/{username}/avatar")]
[Authorize]
- [RequireContentType, RequireContentLength]
+ [RequireContentLength]
[Consumes("image/png", "image/jpeg", "image/gif", "image/webp")]
public async Task<IActionResult> Put([FromRoute][Username] string username)
{
@@ -87,6 +93,17 @@ namespace Timeline.Controllers return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
+ long id;
+ try
+ {
+ id = await _userService.GetUserIdByUsername(username);
+ }
+ catch (UserNotExistException e)
+ {
+ _logger.LogInformation(e, Log.Format(LogPutUserNotExist, ("Username", username)));
+ return BadRequest(ErrorResponse.UserCommon.NotExist());
+ }
+
try
{
var data = new byte[contentLength];
@@ -99,7 +116,7 @@ namespace Timeline.Controllers if (await Request.Body.ReadAsync(extraByte) != 0)
return BadRequest(ErrorResponse.Common.Content.UnmatchedLength_Bigger());
- await _service.SetAvatar(username, new Avatar
+ await _service.SetAvatar(id, new Avatar
{
Data = data,
Type = Request.ContentType
@@ -109,11 +126,6 @@ namespace Timeline.Controllers ("Username", username), ("Mime Type", Request.ContentType)));
return Ok();
}
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogPutUserNotExist, ("Username", username)));
- return BadRequest(ErrorResponse.UserCommon.NotExist());
- }
catch (AvatarFormatException e)
{
_logger.LogInformation(e, Log.Format(LogPutUserBadFormat, ("Username", username)));
@@ -139,16 +151,19 @@ namespace Timeline.Controllers return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
+ long id;
try
{
- await _service.SetAvatar(username, null);
- return Ok();
+ id = await _userService.GetUserIdByUsername(username);
}
catch (UserNotExistException e)
{
_logger.LogInformation(e, Log.Format(LogDeleteNotExist, ("Username", username)));
return BadRequest(ErrorResponse.UserCommon.NotExist());
}
+
+ await _service.SetAvatar(id, null);
+ return Ok();
}
}
}
diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs index 4c585198..400a518c 100644 --- a/Timeline/Controllers/UserController.cs +++ b/Timeline/Controllers/UserController.cs @@ -1,3 +1,4 @@ +using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -17,30 +18,40 @@ namespace Timeline.Controllers [ApiController]
public class UserController : Controller
{
-
private readonly ILogger<UserController> _logger;
private readonly IUserService _userService;
+ private readonly IMapper _mapper;
- public UserController(ILogger<UserController> logger, IUserService userService)
+ public UserController(ILogger<UserController> logger, IUserService userService, IMapper mapper)
{
_logger = logger;
_userService = userService;
+ _mapper = mapper;
+ }
+
+ private IUserInfo ConvertToUserInfo(User user, bool administrator)
+ {
+ if (administrator)
+ return _mapper.Map<UserInfoForAdmin>(user);
+ else
+ return _mapper.Map<UserInfo>(user);
}
[HttpGet("users")]
- public async Task<ActionResult<User[]>> List()
+ public async Task<ActionResult<IUserInfo[]>> List()
{
var users = await _userService.GetUsers();
- return Ok(users.Select(u => u.EraseSecretAndFinalFill(Url, this.IsAdministrator())).ToArray());
+ var administrator = this.IsAdministrator();
+ return Ok(users.Select(u => ConvertToUserInfo(u, administrator)).ToArray());
}
[HttpGet("users/{username}")]
- public async Task<ActionResult<User>> Get([FromRoute][Username] string username)
+ public async Task<ActionResult<IUserInfo>> Get([FromRoute][Username] string username)
{
try
{
var user = await _userService.GetUserByUsername(username);
- return Ok(user.EraseSecretAndFinalFill(Url, this.IsAdministrator()));
+ return Ok(ConvertToUserInfo(user, this.IsAdministrator()));
}
catch (UserNotExistException e)
{
@@ -52,22 +63,11 @@ namespace Timeline.Controllers [HttpPatch("users/{username}"), Authorize]
public async Task<ActionResult> Patch([FromBody] UserPatchRequest body, [FromRoute][Username] string username)
{
- static User Convert(UserPatchRequest body)
- {
- return new User
- {
- Username = body.Username,
- Password = body.Password,
- Administrator = body.Administrator,
- Nickname = body.Nickname
- };
- }
-
if (this.IsAdministrator())
{
try
{
- await _userService.ModifyUser(username, Convert(body));
+ await _userService.ModifyUser(username, _mapper.Map<User>(body));
return Ok();
}
catch (UserNotExistException e)
@@ -75,6 +75,10 @@ namespace Timeline.Controllers _logger.LogInformation(e, Log.Format(LogPatchUserNotExist, ("Username", username)));
return NotFound(ErrorResponse.UserCommon.NotExist());
}
+ catch (ConflictException)
+ {
+ return BadRequest(ErrorResponse.UserController.UsernameConflict());
+ }
}
else
{
@@ -94,7 +98,7 @@ namespace Timeline.Controllers return StatusCode(StatusCodes.Status403Forbidden,
ErrorResponse.Common.CustomMessage_Forbid(UserController_Patch_Forbid_Administrator));
- await _userService.ModifyUser(this.GetUserId(), Convert(body));
+ await _userService.ModifyUser(this.GetUserId(), _mapper.Map<User>(body));
return Ok();
}
}
@@ -113,10 +117,18 @@ namespace Timeline.Controllers }
}
- [HttpPost("userop/create"), AdminAuthorize]
- public async Task<ActionResult> CreateUser([FromBody] User body)
+ [HttpPost("userop/createuser"), AdminAuthorize]
+ public async Task<ActionResult> CreateUser([FromBody] CreateUserRequest body)
{
-
+ try
+ {
+ await _userService.CreateUser(_mapper.Map<User>(body));
+ return Ok();
+ }
+ catch (ConflictException)
+ {
+ return BadRequest(ErrorResponse.UserController.UsernameConflict());
+ }
}
[HttpPost("userop/changepassword"), Authorize]
@@ -133,7 +145,7 @@ namespace Timeline.Controllers ("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.
+ // User can't be non-existent or the token is bad.
}
}
}
|