aboutsummaryrefslogtreecommitdiff
path: root/Timeline
diff options
context:
space:
mode:
Diffstat (limited to 'Timeline')
-rw-r--r--Timeline/Controllers/UserAvatarController.cs26
-rw-r--r--Timeline/Filters/ContentHeaderAttributes.cs48
-rw-r--r--Timeline/Models/Http/Common.cs19
3 files changed, 89 insertions, 4 deletions
diff --git a/Timeline/Controllers/UserAvatarController.cs b/Timeline/Controllers/UserAvatarController.cs
index 89d2650c..ffadcb86 100644
--- a/Timeline/Controllers/UserAvatarController.cs
+++ b/Timeline/Controllers/UserAvatarController.cs
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
using Timeline.Authenticate;
+using Timeline.Filters;
using Timeline.Models.Http;
using Timeline.Services;
@@ -22,6 +23,9 @@ namespace Timeline.Controllers
public const int Put_BadFormat_CantDecode = -2011;
public const int Put_BadFormat_UnmatchedFormat = -2012;
public const int Put_BadFormat_BadSize = -2013;
+ public const int Put_Content_TooBig = -2021;
+ public const int Put_Content_UnmatchedLength_Less = -2022;
+ public const int Put_Content_UnmatchedLength_Bigger = -2023;
public const int Delete_UserNotExist = -3001;
public const int Delete_Forbid = -3002;
@@ -55,7 +59,7 @@ namespace Timeline.Controllers
[HttpGet("users/{username}/avatar")]
[Authorize]
- public async Task<IActionResult> Get(string username)
+ public async Task<IActionResult> Get([FromRoute] string username)
{
const string IfModifiedSinceHeaderKey = "If-Modified-Since";
try
@@ -83,9 +87,15 @@ namespace Timeline.Controllers
[HttpPut("users/{username}/avatar")]
[Authorize]
+ [RequireContentType, RequireContentLength]
[Consumes("image/png", "image/jpeg", "image/gif", "image/webp")]
public async Task<IActionResult> Put(string username)
{
+ var contentLength = Request.ContentLength.Value;
+ if (contentLength > 1000 * 1000 * 10)
+ return BadRequest(new CommonResponse(ErrorCodes.Put_Content_TooBig,
+ "Content can't be bigger than 10MB."));
+
if (!User.IsAdmin() && User.Identity.Name != username)
{
_logger.LogInformation($"Attempt to put a avatar of other user as a non-admin failed. Operator Username: {User.Identity.Name} ; Username To Put Avatar: {username} .");
@@ -95,8 +105,16 @@ namespace Timeline.Controllers
try
{
- var data = new byte[Convert.ToInt32(Request.ContentLength)];
- await Request.Body.ReadAsync(data, 0, data.Length);
+ var data = new byte[contentLength];
+ var bytesRead = await Request.Body.ReadAsync(data);
+
+ if (bytesRead != contentLength)
+ return BadRequest(new CommonResponse(ErrorCodes.Put_Content_UnmatchedLength_Less,
+ $"Content length in header is {contentLength} but actual length is {bytesRead}."));
+
+ if (Request.Body.ReadByte() != -1)
+ return BadRequest(new CommonResponse(ErrorCodes.Put_Content_UnmatchedLength_Bigger,
+ $"Content length in header is {contentLength} but actual length is bigger than that."));
await _service.SetAvatar(username, new Avatar
{
@@ -121,7 +139,7 @@ namespace Timeline.Controllers
[HttpDelete("users/{username}/avatar")]
[Authorize]
- public async Task<IActionResult> Delete(string username)
+ public async Task<IActionResult> Delete([FromRoute] string username)
{
if (!User.IsAdmin() && User.Identity.Name != username)
{
diff --git a/Timeline/Filters/ContentHeaderAttributes.cs b/Timeline/Filters/ContentHeaderAttributes.cs
new file mode 100644
index 00000000..14685a01
--- /dev/null
+++ b/Timeline/Filters/ContentHeaderAttributes.cs
@@ -0,0 +1,48 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Timeline.Models.Http;
+
+namespace Timeline.Filters
+{
+ public class RequireContentTypeAttribute : ActionFilterAttribute
+ {
+ public override void OnActionExecuting(ActionExecutingContext context)
+ {
+ if (context.HttpContext.Request.ContentType == null)
+ {
+ context.Result = new BadRequestObjectResult(CommonResponse.MissingContentType());
+ }
+ }
+ }
+
+ public class RequireContentLengthAttribute : ActionFilterAttribute
+ {
+ public RequireContentLengthAttribute()
+ : this(true)
+ {
+
+ }
+
+ public RequireContentLengthAttribute(bool requireNonZero)
+ {
+ RequireNonZero = requireNonZero;
+ }
+
+ public bool RequireNonZero { get; set; }
+
+ public override void OnActionExecuting(ActionExecutingContext context)
+ {
+ if (context.HttpContext.Request.ContentLength == null)
+ {
+ context.Result = new BadRequestObjectResult(CommonResponse.MissingContentLength());
+ return;
+ }
+
+ if (RequireNonZero && context.HttpContext.Request.ContentLength.Value == 0)
+ {
+ context.Result = new BadRequestObjectResult(CommonResponse.ZeroContentLength());
+ return;
+ }
+ }
+ }
+}
diff --git a/Timeline/Models/Http/Common.cs b/Timeline/Models/Http/Common.cs
index b4932754..50f6836e 100644
--- a/Timeline/Models/Http/Common.cs
+++ b/Timeline/Models/Http/Common.cs
@@ -9,6 +9,10 @@ namespace Timeline.Models.Http
/// For example a required field is null.
/// </summary>
public const int InvalidModel = -100;
+
+ public const int Header_Missing_ContentType = -111;
+ public const int Header_Missing_ContentLength = -112;
+ public const int Header_Zero_ContentLength = -113;
}
public static CommonResponse InvalidModel(string message)
@@ -16,6 +20,21 @@ namespace Timeline.Models.Http
return new CommonResponse(ErrorCodes.InvalidModel, message);
}
+ public static CommonResponse MissingContentType()
+ {
+ return new CommonResponse(ErrorCodes.Header_Missing_ContentType, "Header Content-Type is required.");
+ }
+
+ public static CommonResponse MissingContentLength()
+ {
+ return new CommonResponse(ErrorCodes.Header_Missing_ContentLength, "Header Content-Length is missing or of bad format.");
+ }
+
+ public static CommonResponse ZeroContentLength()
+ {
+ return new CommonResponse(ErrorCodes.Header_Zero_ContentLength, "Header Content-Length must not be 0.");
+ }
+
public CommonResponse()
{