mirror of
https://github.com/QuotifyTeam/QuotifyBE.git
synced 2025-12-16 15:40:07 +01:00
feat: naive sanity check for image URLs
This commit is contained in:
@@ -158,32 +158,47 @@ public class QuotesController : ControllerBase
|
|||||||
/// [AUTHED] Add a new quote
|
/// [AUTHED] Add a new quote
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Newly created quote's id</returns>
|
/// <returns>Newly created quote's id</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// <b>Note</b>:
|
||||||
|
/// User-provided image URLs are validated by checking
|
||||||
|
/// if they start with "https://", "http://" or "/".
|
||||||
|
/// This is rather a naive solution.
|
||||||
|
/// </remarks>
|
||||||
/// <param name="request">Form data containing required quote information</param>
|
/// <param name="request">Form data containing required quote information</param>
|
||||||
/// <response code="201">Returned on valid request</response>
|
/// <response code="201">Returned on valid request</response>
|
||||||
/// <response code="400">Returned when any of the categories does not exist</response>
|
/// <response code="400">Returned when any of the categories does not exist</response>
|
||||||
/// <response code="403">Returned when user's id does not match the creator's id</response>
|
/// <response code="403">Returned when user's id does not match the creator's id</response>
|
||||||
|
/// <response code="406">Returned when image url is invalid (does not start with "https://", "http://", or "/")</response>
|
||||||
[HttpPost("new")]
|
[HttpPost("new")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[EnableCors]
|
[EnableCors]
|
||||||
[ProducesResponseType(201)]
|
[ProducesResponseType(201)]
|
||||||
[ProducesResponseType(typeof(ErrorDTO), 400)]
|
[ProducesResponseType(typeof(ErrorDTO), 400)]
|
||||||
[ProducesResponseType(typeof(ErrorDTO), 403)]
|
[ProducesResponseType(typeof(ErrorDTO), 403)]
|
||||||
|
[ProducesResponseType(typeof(ErrorDTO), 406)]
|
||||||
public async Task<IActionResult> CreateQuote([FromBody] CreateQuoteDTO request)
|
public async Task<IActionResult> CreateQuote([FromBody] CreateQuoteDTO request)
|
||||||
{
|
{
|
||||||
// Get user ID from claims
|
// Get user ID from claims
|
||||||
|
|
||||||
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||||
if (userIdClaim == null || !int.TryParse(userIdClaim, out int userId))
|
if (userIdClaim == null || !int.TryParse(userIdClaim, out int userId))
|
||||||
// https://stackoverflow.com/a/47708867
|
// https://stackoverflow.com/a/47708867
|
||||||
return StatusCode(403, new ErrorDTO { Status = "error", Error_msg = "Invalid user ID" });
|
return StatusCode(403, new ErrorDTO { Status = "error", Error_msg = "Invalid user ID" });
|
||||||
|
|
||||||
// Find or create image
|
// Try to find the image inside the DB
|
||||||
Image? image = null;
|
Image? image = null;
|
||||||
if (!string.IsNullOrEmpty(request.ImageUrl))
|
if (!string.IsNullOrEmpty(request.ImageUrl))
|
||||||
{
|
{
|
||||||
image = await _db.Images.FirstOrDefaultAsync(i => i.Url == request.ImageUrl);
|
image = await _db.Images.FirstOrDefaultAsync(i => i.Url == request.ImageUrl);
|
||||||
|
|
||||||
|
// Failed? Just insert it yourself
|
||||||
if (image == null)
|
if (image == null)
|
||||||
{
|
{
|
||||||
|
// Simple (naive) sanity check for image URLs
|
||||||
|
if ( !request.ImageUrl.StartsWith("http://")
|
||||||
|
&& !request.ImageUrl.StartsWith("https://")
|
||||||
|
&& !request.ImageUrl.StartsWith("/"))
|
||||||
|
return StatusCode(406, new ErrorDTO { Status = "error", Error_msg = "Image URLs should point to http/https url or a local resource" });
|
||||||
|
|
||||||
image = new Image { Url = request.ImageUrl };
|
image = new Image { Url = request.ImageUrl };
|
||||||
_db.Images.Add(image);
|
_db.Images.Add(image);
|
||||||
await _db.SaveChangesAsync();
|
await _db.SaveChangesAsync();
|
||||||
@@ -347,13 +362,19 @@ public class QuotesController : ControllerBase
|
|||||||
/// While "categories = null" will not alter the quote's categories,
|
/// While "categories = null" will not alter the quote's categories,
|
||||||
/// "categories = []" will (and in turn, empty each and every present category)!<br/>
|
/// "categories = []" will (and in turn, empty each and every present category)!<br/>
|
||||||
/// Be careful when handling user-provided categories!
|
/// Be careful when handling user-provided categories!
|
||||||
|
/// <br/><br/>
|
||||||
|
/// <b>Note</b>:
|
||||||
|
/// User-provided image URLs are validated by checking
|
||||||
|
/// if they start with "https://", "http://" or "/".
|
||||||
|
/// This is rather a naive solution.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <returns>Newly modified quote as a DTO</returns>
|
/// <returns>Newly modified quote as a DTO</returns>
|
||||||
/// <param name="id">Quote to be modified</param>
|
/// <param name="id">Quote to be modified</param>
|
||||||
/// <param name="updatedQuote">Updated quote form data</param>
|
/// <param name="updatedQuote">Updated quote form data. Id is ignored.</param>
|
||||||
/// <response code="204">Returned on valid request</response>
|
/// <response code="204">Returned on valid request</response>
|
||||||
/// <response code="400">Returned when request text or author is empty (or whitespace)</response>
|
/// <response code="400">Returned when request text or author is empty (or whitespace)</response>
|
||||||
/// <response code="404">Returned when no such quote exists</response>
|
/// <response code="404">Returned when no such quote exists</response>
|
||||||
|
/// <response code="406">Returned when image url is invalid (does not start with "https://", "http://", or "/")</response>
|
||||||
[HttpPatch("{id}")]
|
[HttpPatch("{id}")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[EnableCors]
|
[EnableCors]
|
||||||
@@ -385,9 +406,16 @@ public class QuotesController : ControllerBase
|
|||||||
if (!string.IsNullOrEmpty(updatedQuote.ImageUrl))
|
if (!string.IsNullOrEmpty(updatedQuote.ImageUrl))
|
||||||
{
|
{
|
||||||
image = await _db.Images.FirstOrDefaultAsync(i => i.Url == updatedQuote.ImageUrl);
|
image = await _db.Images.FirstOrDefaultAsync(i => i.Url == updatedQuote.ImageUrl);
|
||||||
|
|
||||||
// Failed? Just insert it yourself
|
// Failed? Just insert it yourself
|
||||||
if (image == null)
|
if (image == null)
|
||||||
{
|
{
|
||||||
|
// Simple (naive) sanity check for image URLs
|
||||||
|
if ( !updatedQuote.ImageUrl.StartsWith("http://")
|
||||||
|
&& !updatedQuote.ImageUrl.StartsWith("https://")
|
||||||
|
&& !updatedQuote.ImageUrl.StartsWith("/"))
|
||||||
|
return StatusCode(406, new ErrorDTO { Status = "error", Error_msg = "Image URLs should point to http/https url or a local resource" });
|
||||||
|
|
||||||
image = new Image { Url = updatedQuote.ImageUrl };
|
image = new Image { Url = updatedQuote.ImageUrl };
|
||||||
_db.Images.Add(image);
|
_db.Images.Add(image);
|
||||||
await _db.SaveChangesAsync();
|
await _db.SaveChangesAsync();
|
||||||
|
|||||||
Reference in New Issue
Block a user