Force contentType from the headers to be .png, .jpeg or .jpg

This commit is contained in:
Dominic Villemure
2024-07-10 01:38:27 -04:00
parent 0c549fd262
commit d2c6209954
8 changed files with 73 additions and 34 deletions

View File

@@ -2,7 +2,7 @@
public static class CommonFileNames public static class CommonFileNames
{ {
public static string ProfilePicture = "profilePicture.png"; public static string ProfilePicture = "profilePicture";
public static string BannerPicture = "bannerPicture.png"; public static string BannerPicture = "bannerPicture";
public static string WebsiteIcon = "websiteIcon.png"; public static string WebsiteIcon = "websiteIcon";
} }

View File

@@ -1,6 +0,0 @@
namespace Hutopy.Application.AzureBlobStorage.Constants;
public static class ContentTypes
{
public static string ImagePng = "image/png";
}

View File

@@ -1,5 +1,6 @@
using Hutopy.Application.AzureBlobStorage.Constants; using Hutopy.Application.AzureBlobStorage.Constants;
using Hutopy.Application.Common.Interfaces; using Hutopy.Application.Common.Interfaces;
using Hutopy.Application.Utils;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
namespace Hutopy.Application.Users.Commands; namespace Hutopy.Application.Users.Commands;
@@ -13,10 +14,12 @@ public class UploadBannerPictureCommand : IRequest<IResult>
public string BannerPictureUrl { get; init; } = string.Empty; public string BannerPictureUrl { get; init; } = string.Empty;
} }
public class UploadBannerPictureCommandHandler(IIdentityService identityService, IAzureBlobStorageService azureBlobStorageService) : IRequestHandler<UploadBannerPictureCommand, IResult> public class UploadBannerPictureCommandHandler(IHttpContextAccessor contextAccessor, IIdentityService identityService, IAzureBlobStorageService azureBlobStorageService) : IRequestHandler<UploadBannerPictureCommand, IResult>
{ {
public async Task<IResult> Handle(UploadBannerPictureCommand request, CancellationToken cancellationToken) public async Task<IResult> Handle(UploadBannerPictureCommand request, CancellationToken cancellationToken)
{ {
var contentType = contextAccessor.EnsureContentType();
// If an url to the picture is provided, use it right away and don't upload anything. // If an url to the picture is provided, use it right away and don't upload anything.
if (!string.IsNullOrEmpty(request.BannerPictureUrl)) if (!string.IsNullOrEmpty(request.BannerPictureUrl))
{ {
@@ -29,22 +32,11 @@ public class UploadBannerPictureCommandHandler(IIdentityService identityService,
var blobName = $"{currentUserId}/{SubDirectoryNames.Profile}/{CommonFileNames.BannerPicture}"; var blobName = $"{currentUserId}/{SubDirectoryNames.Profile}/{CommonFileNames.BannerPicture}";
try var url = await azureBlobStorageService.UploadFileAsync(ContainerNames.Users, blobName, request.BannerPicture, contentType);
{
var url = await azureBlobStorageService.UploadFileAsync(ContainerNames.Users, blobName, request.BannerPicture, ContentTypes.ImagePng);
await identityService.UpdateCurrentUserBannerPictureUrlAsync(url); await identityService.UpdateCurrentUserBannerPictureUrlAsync(url);
return Results.Ok(url); return Results.Ok(url);
} }
catch (InvalidOperationException ex)
{
return Results.BadRequest(ex.Message);
}
catch (Exception)
{
return Results.StatusCode(StatusCodes.Status500InternalServerError);
}
}
} }

View File

@@ -1,5 +1,6 @@
using Hutopy.Application.AzureBlobStorage.Constants; using Hutopy.Application.AzureBlobStorage.Constants;
using Hutopy.Application.Common.Interfaces; using Hutopy.Application.Common.Interfaces;
using Hutopy.Application.Utils;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
namespace Hutopy.Application.Users.Commands; namespace Hutopy.Application.Users.Commands;
@@ -13,10 +14,12 @@ public class UploadProfilePictureCommand : IRequest<IResult>
public string ProfilePictureUrl { get; init; } = string.Empty; public string ProfilePictureUrl { get; init; } = string.Empty;
} }
public class UploadProfilePictureCommandHandler(IIdentityService identityService, IAzureBlobStorageService azureBlobStorageService) : IRequestHandler<UploadProfilePictureCommand, IResult> public class UploadProfilePictureCommandHandler(IHttpContextAccessor contextAccessor, IIdentityService identityService, IAzureBlobStorageService azureBlobStorageService) : IRequestHandler<UploadProfilePictureCommand, IResult>
{ {
public async Task<IResult> Handle(UploadProfilePictureCommand request, CancellationToken cancellationToken) public async Task<IResult> Handle(UploadProfilePictureCommand request, CancellationToken cancellationToken)
{ {
var contentType = contextAccessor.EnsureContentType();
// If an url to the picture is provided, use it right away and don't upload anything. // If an url to the picture is provided, use it right away and don't upload anything.
if (!string.IsNullOrEmpty(request.ProfilePictureUrl)) if (!string.IsNullOrEmpty(request.ProfilePictureUrl))
{ {
@@ -29,7 +32,7 @@ public class UploadProfilePictureCommandHandler(IIdentityService identityService
var blobName = $"{currentUserId}/{SubDirectoryNames.Profile}/{CommonFileNames.ProfilePicture}"; var blobName = $"{currentUserId}/{SubDirectoryNames.Profile}/{CommonFileNames.ProfilePicture}";
var url = await azureBlobStorageService.UploadFileAsync(ContainerNames.Users, blobName, request.ProfilePicture, ContentTypes.ImagePng); var url = await azureBlobStorageService.UploadFileAsync(ContainerNames.Users, blobName, request.ProfilePicture, contentType);
await identityService.UpdateCurrentUserProfilePictureUrlAsync(url); await identityService.UpdateCurrentUserProfilePictureUrlAsync(url);

View File

@@ -1,5 +1,6 @@
using Hutopy.Application.AzureBlobStorage.Constants; using Hutopy.Application.AzureBlobStorage.Constants;
using Hutopy.Application.Common.Interfaces; using Hutopy.Application.Common.Interfaces;
using Hutopy.Application.Utils;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
namespace Hutopy.Application.Users.Commands; namespace Hutopy.Application.Users.Commands;
@@ -14,10 +15,12 @@ public class UploadWebsiteIconCommand : IRequest<IResult>
public string WebsitePictureUrl { get; init; } = string.Empty; public string WebsitePictureUrl { get; init; } = string.Empty;
} }
public class UploadWebsiteIconCommandHandler(IIdentityService identityService, IAzureBlobStorageService azureBlobStorageService) : IRequestHandler<UploadWebsiteIconCommand, IResult> public class UploadWebsiteIconCommandHandler(IHttpContextAccessor contextAccessor, IIdentityService identityService, IAzureBlobStorageService azureBlobStorageService) : IRequestHandler<UploadWebsiteIconCommand, IResult>
{ {
public async Task<IResult> Handle(UploadWebsiteIconCommand request, CancellationToken cancellationToken) public async Task<IResult> Handle(UploadWebsiteIconCommand request, CancellationToken cancellationToken)
{ {
var contentType = contextAccessor.EnsureContentType();
// If an url to the picture is provided, use it right away and don't upload anything. // If an url to the picture is provided, use it right away and don't upload anything.
if (!string.IsNullOrEmpty(request.WebsitePictureUrl)) if (!string.IsNullOrEmpty(request.WebsitePictureUrl))
{ {
@@ -30,7 +33,7 @@ public class UploadWebsiteIconCommandHandler(IIdentityService identityService, I
var blobName = $"{currentUserId}/{SubDirectoryNames.Profile}/{CommonFileNames.WebsiteIcon}"; var blobName = $"{currentUserId}/{SubDirectoryNames.Profile}/{CommonFileNames.WebsiteIcon}";
var url = await azureBlobStorageService.UploadFileAsync(ContainerNames.Users, blobName, request.WebsiteIcon, ContentTypes.ImagePng); var url = await azureBlobStorageService.UploadFileAsync(ContainerNames.Users, blobName, request.WebsiteIcon, contentType);
await identityService.UpdateCurrentUserWebsiteIconUrlAsync(url); await identityService.UpdateCurrentUserWebsiteIconUrlAsync(url);

View File

@@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Http;
namespace Hutopy.Application.Utils;
public static class HttpContextAccessorExtensions
{
public static HttpContext EnsureHttpContext(this IHttpContextAccessor httpContextAccessor)
{
if (httpContextAccessor.HttpContext == null)
{
throw new InvalidOperationException("HttpContext is null.");
}
return httpContextAccessor.HttpContext;
}
public static string EnsureContentType(this IHttpContextAccessor httpContextAccessor)
{
var httpContext = EnsureHttpContext(httpContextAccessor);
var contentType = httpContext.Request.ContentType;
if (string.IsNullOrEmpty(contentType))
{
throw new InvalidOperationException("Content-Type header is missing.");
}
return contentType;
}
}

View File

@@ -1,6 +1,3 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Azure; using Azure;
using Azure.Storage.Blobs; using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models; using Azure.Storage.Blobs.Models;
@@ -16,7 +13,6 @@ public class AzureBlobStorageService : IAzureBlobStorageService
private readonly ILogger<AzureBlobStorageService> _logger; private readonly ILogger<AzureBlobStorageService> _logger;
private readonly long _maxUploadSize = 10 * 1024 * 1024; // 10 MB in bytes private readonly long _maxUploadSize = 10 * 1024 * 1024; // 10 MB in bytes
public AzureBlobStorageService(IConfiguration configuration, ILogger<AzureBlobStorageService> logger) public AzureBlobStorageService(IConfiguration configuration, ILogger<AzureBlobStorageService> logger)
{ {
_logger = logger; _logger = logger;
@@ -47,6 +43,13 @@ public class AzureBlobStorageService : IAzureBlobStorageService
throw new InvalidOperationException($"Blob storage: File size exceeds the maximum allowed size of {_maxUploadSize} bytes."); throw new InvalidOperationException($"Blob storage: File size exceeds the maximum allowed size of {_maxUploadSize} bytes.");
} }
// Validate content type
if (!ContentTypes.IsAllowed(contentType))
{
_logger.LogInformation($"Blob storage: Unsupported file type {contentType}. Only PNG and JPEG are allowed.");
throw new InvalidOperationException("Unsupported file type. Only PNG and JPEG are allowed.");
}
try try
{ {
// Get a reference to a container // Get a reference to a container

View File

@@ -0,0 +1,15 @@
namespace Hutopy.Infrastructure.AzureBlob;
public static class ContentTypes
{
private static string ImagePng = "image/png";
private static string ImageJpeg = "image/jpeg";
private static string ImageJpg = "image/jpg";
public static HashSet<string> AllowedContentTypes = new HashSet<string> { ImagePng, ImageJpeg, ImageJpg };
public static bool IsAllowed(string contentType)
{
return AllowedContentTypes.Contains(contentType);
}
}