diff options
author | crupest <crupest@outlook.com> | 2020-02-02 22:37:47 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2020-02-02 22:37:47 +0800 |
commit | 12f85448cde94d70d9030b757b09caa5e2f53061 (patch) | |
tree | 38f99c0b8631d34facfe81c23bab274168643040 | |
parent | cb18564275735da0614be39371e2e3e7f5467e88 (diff) | |
download | timeline-12f85448cde94d70d9030b757b09caa5e2f53061.tar.gz timeline-12f85448cde94d70d9030b757b09caa5e2f53061.tar.bz2 timeline-12f85448cde94d70d9030b757b09caa5e2f53061.zip |
...
-rw-r--r-- | Timeline/Controllers/PersonalTimelineController.cs | 10 | ||||
-rw-r--r-- | Timeline/Controllers/TimelineController.cs | 131 | ||||
-rw-r--r-- | Timeline/Models/Http/TimelineCommon.cs | 17 | ||||
-rw-r--r-- | Timeline/Services/TimelineService.cs | 31 |
4 files changed, 183 insertions, 6 deletions
diff --git a/Timeline/Controllers/PersonalTimelineController.cs b/Timeline/Controllers/PersonalTimelineController.cs index 842da015..8cf098bf 100644 --- a/Timeline/Controllers/PersonalTimelineController.cs +++ b/Timeline/Controllers/PersonalTimelineController.cs @@ -28,7 +28,7 @@ namespace Timeline.Controllers [HttpGet("users/{username}/timeline")]
public async Task<ActionResult<TimelineInfo>> TimelineGet([FromRoute][Username] string username)
{
- return (await _service.GetTimeline(username)).FillLinks(Url);
+ return (await _service.GetTimeline(username)).FillLinksForPersonalTimeline(Url);
}
[HttpGet("users/{username}/timeline/posts")]
@@ -79,12 +79,12 @@ namespace Timeline.Controllers [Authorize]
public async Task<ActionResult<TimelineInfo>> TimelinePatch([FromRoute][Username] string username, [FromBody] TimelinePatchRequest body)
{
- if (!this.IsAdministrator() && !(User.Identity.Name == username))
+ if (!this.IsAdministrator() && !(await _service.HasManagePermission(username, this.GetUserId())))
{
return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
await _service.ChangeProperty(username, body);
- var timeline = (await _service.GetTimeline(username)).FillLinks(Url);
+ var timeline = (await _service.GetTimeline(username)).FillLinksForPersonalTimeline(Url);
return Ok(timeline);
}
@@ -92,7 +92,7 @@ namespace Timeline.Controllers [Authorize]
public async Task<ActionResult> TimelineMemberPut([FromRoute][Username] string username, [FromRoute][Username] string member)
{
- if (!this.IsAdministrator() && !(User.Identity.Name == username))
+ if (!this.IsAdministrator() && !(await _service.HasManagePermission(username, this.GetUserId())))
{
return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
@@ -112,7 +112,7 @@ namespace Timeline.Controllers [Authorize]
public async Task<ActionResult> TimelineMemberDelete([FromRoute][Username] string username, [FromRoute][Username] string member)
{
- if (!this.IsAdministrator() && !(User.Identity.Name == username))
+ if (!this.IsAdministrator() && !(await _service.HasManagePermission(username, this.GetUserId())))
{
return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
diff --git a/Timeline/Controllers/TimelineController.cs b/Timeline/Controllers/TimelineController.cs new file mode 100644 index 00000000..be271de7 --- /dev/null +++ b/Timeline/Controllers/TimelineController.cs @@ -0,0 +1,131 @@ +using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Timeline.Models.Http;
+using Timeline.Models.Validation;
+using Timeline.Services;
+
+namespace Timeline.Controllers
+{
+ [ApiController]
+ public class TimelineController : Controller
+ {
+ private readonly ILogger<TimelineController> _logger;
+
+ private readonly ITimelineService _service;
+
+ public TimelineController(ILogger<TimelineController> logger, ITimelineService service)
+ {
+ _logger = logger;
+ _service = service;
+ }
+
+ [HttpGet("timelines/{name}")]
+ public async Task<ActionResult<TimelineInfo>> TimelineGet([FromRoute][TimelineName] string name)
+ {
+ return (await _service.GetTimeline(name)).FillLinksForNormalTimeline(Url);
+ }
+
+ [HttpGet("timelines/{name}/posts")]
+ public async Task<ActionResult<IList<TimelinePostInfo>>> PostListGet([FromRoute][TimelineName] string name)
+ {
+ if (!this.IsAdministrator() && !await _service.HasReadPermission(name, this.GetOptionalUserId()))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
+
+ return await _service.GetPosts(name);
+ }
+
+ [HttpPost("timelines/{name}/posts")]
+ [Authorize]
+ public async Task<ActionResult<TimelinePostInfo>> PostPost([FromRoute][TimelineName] string name, [FromBody] TimelinePostCreateRequest body)
+ {
+ var id = this.GetUserId();
+ if (!this.IsAdministrator() && !await _service.IsMemberOf(name, id))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
+
+ var res = await _service.CreatePost(name, id, body.Content, body.Time);
+ return res;
+ }
+
+ [HttpDelete("timelines/{name}/posts/{id}")]
+ [Authorize]
+ public async Task<ActionResult> PostDelete([FromRoute][TimelineName] string name, [FromRoute] long id)
+ {
+ try
+ {
+ if (!this.IsAdministrator() && !await _service.HasPostModifyPermission(name, id, this.GetUserId()))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
+ await _service.DeletePost(name, id);
+ return Ok(CommonDeleteResponse.Delete());
+ }
+ catch (TimelinePostNotExistException)
+ {
+ return Ok(CommonDeleteResponse.NotExist());
+ }
+ }
+
+ [HttpPatch("timelines/{name}")]
+ [Authorize]
+ public async Task<ActionResult<TimelineInfo>> TimelinePatch([FromRoute][TimelineName] string name, [FromBody] TimelinePatchRequest body)
+ {
+ if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
+ await _service.ChangeProperty(name, body);
+ var timeline = (await _service.GetTimeline(name)).FillLinksForNormalTimeline(Url);
+ return Ok(timeline);
+ }
+
+ [HttpPut("timelines/{name}/members/{member}")]
+ [Authorize]
+ public async Task<ActionResult> TimelineMemberPut([FromRoute][TimelineName] string name, [FromRoute][Username] string member)
+ {
+ if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
+
+ try
+ {
+ await _service.ChangeMember(name, new List<string> { member }, null);
+ return Ok();
+ }
+ catch (UserNotExistException)
+ {
+ return BadRequest(ErrorResponse.TimelineController.MemberPut_NotExist());
+ }
+ }
+
+ [HttpDelete("timelines/{name}/members/{member}")]
+ [Authorize]
+ public async Task<ActionResult> TimelineMemberDelete([FromRoute][TimelineName] string name, [FromRoute][Username] string member)
+ {
+ if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
+
+ try
+ {
+ await _service.ChangeMember(name, null, new List<string> { member });
+ return Ok(CommonDeleteResponse.Delete());
+ }
+ catch (UserNotExistException)
+ {
+ return Ok(CommonDeleteResponse.NotExist());
+ }
+ }
+
+ // TODO: Create API .
+ }
+}
diff --git a/Timeline/Models/Http/TimelineCommon.cs b/Timeline/Models/Http/TimelineCommon.cs index 0b2a714c..1cb47dac 100644 --- a/Timeline/Models/Http/TimelineCommon.cs +++ b/Timeline/Models/Http/TimelineCommon.cs @@ -52,7 +52,7 @@ namespace Timeline.Models.Http public static class TimelineInfoExtensions
{
- public static TimelineInfo FillLinks(this TimelineInfo info, IUrlHelper urlHelper)
+ public static TimelineInfo FillLinksForPersonalTimeline(this TimelineInfo info, IUrlHelper urlHelper)
{
if (info == null)
throw new ArgumentNullException(nameof(info));
@@ -66,5 +66,20 @@ namespace Timeline.Models.Http return info;
}
+
+ public static TimelineInfo FillLinksForNormalTimeline(this TimelineInfo info, IUrlHelper urlHelper)
+ {
+ if (info == null)
+ throw new ArgumentNullException(nameof(info));
+ if (urlHelper == null)
+ throw new ArgumentNullException(nameof(urlHelper));
+
+ info._links = new TimelineInfoLinks
+ {
+ Posts = urlHelper.ActionLink(nameof(TimelineController.PostListGet), nameof(TimelineController)[0..^nameof(Controller).Length], new { info.Name })
+ };
+
+ return info;
+ }
}
}
diff --git a/Timeline/Services/TimelineService.cs b/Timeline/Services/TimelineService.cs index b031297e..991669ad 100644 --- a/Timeline/Services/TimelineService.cs +++ b/Timeline/Services/TimelineService.cs @@ -139,6 +139,26 @@ namespace Timeline.Services Task ChangeMember(string name, IList<string>? add, IList<string>? remove);
/// <summary>
+ /// Check whether a user can manage(change timeline info, member, ...) a timeline.
+ /// </summary>
+ /// <param name="name"></param>
+ /// <param name="id"></param>
+ /// <returns>True if the user can manage the timeline, otherwise false.</returns>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="name"/> is null.</exception>
+ /// <exception cref="ArgumentException">Thrown when <paramref name="name"/> is illegal. It is not a valid timeline name (for normal timeline service) or a valid username (for personal timeline service).</exception>
+ /// <exception cref="TimelineNotExistException">
+ /// Thrown when timeline does not exist.
+ /// For normal timeline, it means the name does not exist.
+ /// For personal timeline, it means the user of that username does not exist
+ /// and the inner exception should be a <see cref="UserNotExistException"/>.
+ /// </exception>
+ /// <remarks>
+ /// This method does not check whether visitor is administrator.
+ /// Return false if user with user id does not exist.
+ /// </remarks>
+ Task<bool> HasManagePermission(string name, long userId);
+
+ /// <summary>
/// Verify whether a visitor has the permission to read a timeline.
/// </summary>
/// <param name="name">Username or the timeline name. See remarks of <see cref="IBaseTimelineService"/>.</param>
@@ -490,6 +510,17 @@ namespace Timeline.Services await Database.SaveChangesAsync();
}
+ public async Task<bool> HasManagePermission(string name, long userId)
+ {
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+
+ var timelineId = await FindTimelineId(name);
+ var timelineEntity = await Database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.OwnerId }).SingleAsync();
+
+ return userId == timelineEntity.OwnerId;
+ }
+
public async Task<bool> HasReadPermission(string name, long? visitorId)
{
if (name == null)
|