From 307f6f57555fcc57f5ac7fc2f85a42368b2945ee Mon Sep 17 00:00:00 2001 From: Jezz Santos Date: Sat, 11 May 2024 16:20:00 +1200 Subject: [PATCH] Fixing image uploads. Closes #33 --- src/Application.Resources.Shared/Images.cs | 30 +++++++- .../PasswordCredentialsApplication.cs | 12 +++- src/IdentityApplication/Resources.Designer.cs | 9 +++ src/IdentityApplication/Resources.resx | 3 + .../ImagesApplicationSpec.cs | 2 +- src/ImagesApplication/ImagesApplication.cs | 8 +-- .../External/GravatarClientSpec.cs | 6 +- .../External/GravatarHttpServiceClientSpec.cs | 4 +- .../External/GravatarHttpServiceClient.cs | 2 +- .../FileUploadServiceSpec.cs | 68 ++++++++++++++----- .../Extensions/HttpRequestExtensions.cs | 34 ++++++---- .../FileUploadService.cs | 60 ++++++++-------- .../HttpConstants.cs | 1 + ...etRegistrationPersonConfirmationRequest.cs | 2 +- .../OrganizationsApplicationSpec.cs | 6 +- ...fileApplication.DomainEventHandlersSpec.cs | 2 +- .../UserProfileApplicationSpec.cs | 12 ++-- 17 files changed, 177 insertions(+), 84 deletions(-) diff --git a/src/Application.Resources.Shared/Images.cs b/src/Application.Resources.Shared/Images.cs index c9087447..ba2c24a1 100644 --- a/src/Application.Resources.Shared/Images.cs +++ b/src/Application.Resources.Shared/Images.cs @@ -1,4 +1,6 @@ +using System.Net.Http.Headers; using Application.Interfaces.Resources; +using Common.Extensions; namespace Application.Resources.Shared; @@ -19,13 +21,39 @@ public class FileUpload { public required Stream Content { get; set; } - public required string ContentType { get; set; } + public required FileUploadContentType ContentType { get; set; } public string? Filename { get; set; } public long Size { get; set; } } +public class FileUploadContentType +{ + public string? Charset { get; set; } + + public string? MediaType { get; set; } + + public static FileUploadContentType FromContentType(string contentType) + { + if (contentType.HasNoValue()) + { + return new FileUploadContentType(); + } + + if (MediaTypeHeaderValue.TryParse(contentType, out var parsed)) + { + return new FileUploadContentType + { + MediaType = parsed.MediaType, + Charset = parsed.CharSet + }; + } + + return new FileUploadContentType(); + } +} + public class ImageDownload { public required string ContentType { get; set; } diff --git a/src/IdentityApplication/PasswordCredentialsApplication.cs b/src/IdentityApplication/PasswordCredentialsApplication.cs index 83fd1828..69e08ffb 100644 --- a/src/IdentityApplication/PasswordCredentialsApplication.cs +++ b/src/IdentityApplication/PasswordCredentialsApplication.cs @@ -366,8 +366,7 @@ public async Task> CompletePasswordResetAsync(ICallerContext calle #if TESTINGONLY public async Task> GetPersonRegistrationConfirmationAsync( - ICallerContext caller, string userId, - CancellationToken cancellationToken) + ICallerContext caller, string userId, CancellationToken cancellationToken) { var retrieved = await _repository.FindCredentialsByUserIdAsync(userId.ToId(), cancellationToken); if (retrieved.IsFailure) @@ -379,8 +378,15 @@ public async Task> GetPersonRegist { return Error.EntityNotFound(); } - + var credential = retrieved.Value.Value; + var token = credential.VerificationKeep.Token; + + if (!token.HasValue) + { + return Error.PreconditionViolation(Resources.PasswordCredentialsApplication_RegistrationAlreadyVerified); + } + return new PasswordCredentialConfirmation { Token = credential.VerificationKeep.Token, diff --git a/src/IdentityApplication/Resources.Designer.cs b/src/IdentityApplication/Resources.Designer.cs index c1255367..5cc7d642 100644 --- a/src/IdentityApplication/Resources.Designer.cs +++ b/src/IdentityApplication/Resources.Designer.cs @@ -95,6 +95,15 @@ internal static string PasswordCredentialsApplication_InvalidUsername { } } + /// + /// Looks up a localized string similar to The user account has already been verified. + /// + internal static string PasswordCredentialsApplication_RegistrationAlreadyVerified { + get { + return ResourceManager.GetString("PasswordCredentialsApplication_RegistrationAlreadyVerified", resourceCulture); + } + } + /// /// Looks up a localized string similar to The user account has not been verified. /// diff --git a/src/IdentityApplication/Resources.resx b/src/IdentityApplication/Resources.resx index aef53748..2678d380 100644 --- a/src/IdentityApplication/Resources.resx +++ b/src/IdentityApplication/Resources.resx @@ -33,6 +33,9 @@ The user account has not been verified + + The user account has already been verified + The username is not a valid email address diff --git a/src/ImagesApplication.UnitTests/ImagesApplicationSpec.cs b/src/ImagesApplication.UnitTests/ImagesApplicationSpec.cs index ee12faac..36c30f7b 100644 --- a/src/ImagesApplication.UnitTests/ImagesApplicationSpec.cs +++ b/src/ImagesApplication.UnitTests/ImagesApplicationSpec.cs @@ -183,7 +183,7 @@ public async Task WhenUploadImage_ThenReturns() var upload = new FileUpload { Content = content, - ContentType = "image/jpeg", + ContentType = new FileUploadContentType { MediaType = "image/jpeg" }, Filename = "afilename", Size = 99 }; diff --git a/src/ImagesApplication/ImagesApplication.cs b/src/ImagesApplication/ImagesApplication.cs index 4a9dc053..b89539e0 100644 --- a/src/ImagesApplication/ImagesApplication.cs +++ b/src/ImagesApplication/ImagesApplication.cs @@ -126,10 +126,9 @@ public async Task> UpdateImageAsync(ICallerContext caller, } public async Task> UploadImageAsync(ICallerContext caller, FileUpload upload, - string? description, - CancellationToken cancellationToken) + string? description, CancellationToken cancellationToken) { - var created = ImageRoot.Create(_recorder, _idFactory, upload.ContentType); + var created = ImageRoot.Create(_recorder, _idFactory, upload.ContentType.MediaType!); if (created.IsFailure) { return created.Error; @@ -152,7 +151,8 @@ public async Task> UploadImageAsync(ICallerContext caller, } var uploaded = - await _repository.UploadImageAsync(image.Id, upload.ContentType, upload.Content, cancellationToken); + await _repository.UploadImageAsync(image.Id, upload.ContentType.MediaType!, upload.Content, + cancellationToken); if (uploaded.IsFailure) { return uploaded.Error; diff --git a/src/Infrastructure.Shared.UnitTests/ApplicationServices/External/GravatarClientSpec.cs b/src/Infrastructure.Shared.UnitTests/ApplicationServices/External/GravatarClientSpec.cs index af9dca41..495057ed 100644 --- a/src/Infrastructure.Shared.UnitTests/ApplicationServices/External/GravatarClientSpec.cs +++ b/src/Infrastructure.Shared.UnitTests/ApplicationServices/External/GravatarClientSpec.cs @@ -1,5 +1,6 @@ using System.Net; using Application.Interfaces; +using Application.Resources.Shared; using Common; using FluentAssertions; using Infrastructure.Shared.ApplicationServices.External; @@ -90,7 +91,7 @@ public async Task WhenFindAvatarAsyncAndReturnsImage_ThenReturnsUpload() { StatusCode = HttpStatusCode.OK, Content = stream, - ContentType = "acontenttype", + ContentType = HttpConstants.ContentTypes.ImageJpegWithCharset, ContentLength = 999 }); @@ -98,7 +99,8 @@ public async Task WhenFindAvatarAsyncAndReturnsImage_ThenReturnsUpload() await _client.FindAvatarAsync(_caller.Object, "anemailaddress", CancellationToken.None); result.Should().BeSuccess(); - result.Value.Value.ContentType.Should().Be("acontenttype"); + result.Value.Value.ContentType.Should() + .BeEquivalentTo(FileUploadContentType.FromContentType(HttpConstants.ContentTypes.ImageJpegWithCharset)); result.Value.Value.Content.Should().BeSameAs(stream); result.Value.Value.Filename.Should().BeNull(); result.Value.Value.Size.Should().Be(999); diff --git a/src/Infrastructure.Shared.UnitTests/ApplicationServices/External/GravatarHttpServiceClientSpec.cs b/src/Infrastructure.Shared.UnitTests/ApplicationServices/External/GravatarHttpServiceClientSpec.cs index 2d53e7e6..e6eda568 100644 --- a/src/Infrastructure.Shared.UnitTests/ApplicationServices/External/GravatarHttpServiceClientSpec.cs +++ b/src/Infrastructure.Shared.UnitTests/ApplicationServices/External/GravatarHttpServiceClientSpec.cs @@ -47,7 +47,7 @@ public async Task WhenFindAvatarAsync_ThenReturnsUpload() It.IsAny())) .ReturnsAsync(new FileUpload { - ContentType = "acontenttype", + ContentType = new FileUploadContentType { MediaType = "acontenttype" }, Content = stream, Filename = "afilename", Size = 99 @@ -57,7 +57,7 @@ public async Task WhenFindAvatarAsync_ThenReturnsUpload() result.Should().BeSuccess(); result.Value.HasValue.Should().BeTrue(); - result.Value.Value.ContentType.Should().Be("acontenttype"); + result.Value.Value.ContentType.MediaType.Should().Be("acontenttype"); result.Value.Value.Content.Should().BeSameAs(stream); result.Value.Value.Filename.Should().Be("afilename"); result.Value.Value.Size.Should().Be(99); diff --git a/src/Infrastructure.Shared/ApplicationServices/External/GravatarHttpServiceClient.cs b/src/Infrastructure.Shared/ApplicationServices/External/GravatarHttpServiceClient.cs index e00261d5..f6af0cbc 100644 --- a/src/Infrastructure.Shared/ApplicationServices/External/GravatarHttpServiceClient.cs +++ b/src/Infrastructure.Shared/ApplicationServices/External/GravatarHttpServiceClient.cs @@ -134,7 +134,7 @@ public async Task, Error>> FindAvatarAsync(ICallerCo return new FileUpload { - ContentType = contentType, + ContentType = FileUploadContentType.FromContentType(contentType), Content = content, Filename = null, Size = contentLength diff --git a/src/Infrastructure.Web.Api.Common.UnitTests/FileUploadServiceSpec.cs b/src/Infrastructure.Web.Api.Common.UnitTests/FileUploadServiceSpec.cs index 2e1c5316..fbe06641 100644 --- a/src/Infrastructure.Web.Api.Common.UnitTests/FileUploadServiceSpec.cs +++ b/src/Infrastructure.Web.Api.Common.UnitTests/FileUploadServiceSpec.cs @@ -39,7 +39,7 @@ public void WhenGetUploadedFileAndNoContent_ThenReturnsError() new() { Content = stream, - ContentType = "acontenttype", + ContentType = new FileUploadContentType { MediaType = "acontenttype" }, Size = 0, Filename = "afilename" } @@ -63,7 +63,7 @@ public void WhenGetUploadedFileAndTooLarge_ThenReturnsError() new() { Content = stream, - ContentType = "acontenttype", + ContentType = new FileUploadContentType { MediaType = "acontenttype" }, Size = content.Length, Filename = "afilename" } @@ -76,7 +76,7 @@ public void WhenGetUploadedFileAndTooLarge_ThenReturnsError() } [Fact] - public void WhenGetUploadedFileAndNoAllowedContentTypes_ThenReturnsError() + public void WhenGetUploadedFileAndNotAllowedContentTypes_ThenReturnsError() { var content = new byte[] { 0x0, 0x1 }; using var stream = new MemoryStream(content); @@ -85,7 +85,7 @@ public void WhenGetUploadedFileAndNoAllowedContentTypes_ThenReturnsError() new() { Content = stream, - ContentType = "acontenttype", + ContentType = new FileUploadContentType { MediaType = "acontenttype" }, Size = content.Length, Filename = "afilename" } @@ -131,7 +131,7 @@ public void WhenGetUploadedFileAndNoContentType_ThenReturnsError() var result = _service.GetUploadedFile(uploads, 100, new List { "allowed" }); result.Should().BeError(ErrorCode.Validation, - Resources.FileUploadService_DisallowedFileContent.Format(FileUploadService.UnknownContentType)); + Resources.FileUploadService_DisallowedFileContent.Format(FileUploadService.UnknownMediaType)); } [Fact] @@ -144,7 +144,7 @@ public void WhenGetUploadedFileAndNotAllowedContentType_ThenReturnsError() new() { Content = stream, - ContentType = "notallowed", + ContentType = new FileUploadContentType { MediaType = "notallowed" }, Size = content.Length, Filename = "afilename" } @@ -166,16 +166,18 @@ public void WhenGetUploadedFileAndAllowedContentType_ThenReturns() new() { Content = stream, - ContentType = "allowed", + ContentType = FileUploadContentType.FromContentType(HttpConstants.ContentTypes.ImageJpeg), Size = content.Length, Filename = "afilename" } }; - var result = _service.GetUploadedFile(uploads, 100, new List { "allowed" }); + var result = + _service.GetUploadedFile(uploads, 100, new List { HttpConstants.ContentTypes.ImageJpeg }); result.Should().BeSuccess(); - result.Value.ContentType.Should().Be("allowed"); + result.Value.ContentType.Should() + .BeEquivalentTo(FileUploadContentType.FromContentType(HttpConstants.ContentTypes.ImageJpeg)); result.Value.Filename.Should().Be("afilename"); result.Value.Size.Should().Be(2); result.Value.Content.Position.Should().Be(0); @@ -203,7 +205,7 @@ public void WhenGetUploadedFileAndNotAllowedContentType_ReturnsError() new() { Content = stream, - ContentType = HttpConstants.ContentTypes.ImageJpeg, + ContentType = FileUploadContentType.FromContentType(HttpConstants.ContentTypes.ImageJpeg), Size = content.Length, Filename = "afilename" } @@ -226,7 +228,7 @@ public void WhenGetUploadedFileAndContentTypeAndContentDiffers_ReturnsError() new() { Content = stream, - ContentType = HttpConstants.ContentTypes.ImagePng, + ContentType = FileUploadContentType.FromContentType(HttpConstants.ContentTypes.ImagePng), Size = content.Length, Filename = "afilename" } @@ -251,7 +253,7 @@ public void WhenGetUploadedFileAndFilenameExtensionDiffers_ThenReturnsError() new() { Content = stream, - ContentType = HttpConstants.ContentTypes.ImageJpeg, + ContentType = FileUploadContentType.FromContentType(HttpConstants.ContentTypes.ImageJpeg), Size = content.Length, Filename = "afilename.txt" } @@ -275,7 +277,7 @@ public void WhenGetUploadedFileAndFileExtensionMissing_ThenSetsExtension() new() { Content = stream, - ContentType = HttpConstants.ContentTypes.ImageJpeg, + ContentType = FileUploadContentType.FromContentType(HttpConstants.ContentTypes.ImageJpeg), Size = content.Length, Filename = "afilename" } @@ -285,7 +287,8 @@ public void WhenGetUploadedFileAndFileExtensionMissing_ThenSetsExtension() _service.GetUploadedFile(uploads, 1000, new List { HttpConstants.ContentTypes.ImageJpeg }); result.Should().BeSuccess(); - result.Value.ContentType.Should().Be(HttpConstants.ContentTypes.ImageJpeg); + result.Value.ContentType.Should() + .BeEquivalentTo(FileUploadContentType.FromContentType(HttpConstants.ContentTypes.ImageJpeg)); result.Value.Filename.Should().Be("afilename.jpg"); result.Value.Size.Should().Be(103); result.Value.Content.Position.Should().Be(0); @@ -312,7 +315,37 @@ public void WhenGetUploadedFileAndContentTypeMissing_ThenSetsContentType() _service.GetUploadedFile(uploads, 1000, new List { HttpConstants.ContentTypes.ImageJpeg }); result.Should().BeSuccess(); - result.Value.ContentType.Should().Be(HttpConstants.ContentTypes.ImageJpeg); + result.Value.ContentType.Should() + .BeEquivalentTo(FileUploadContentType.FromContentType(HttpConstants.ContentTypes.ImageJpeg)); + result.Value.Filename.Should().Be("afilename.jpg"); + result.Value.Size.Should().Be(103L); + result.Value.Content.Position.Should().Be(0); + } + + [Fact] + public void WhenGetUploadedWithEncoding_ThenReturns() + { + var content = FileUploadService.ImageJpegMagicBytes.Concat(Enumerable.Repeat((byte)0x01, 100)) + .ToArray(); + using var stream = new MemoryStream(content); + var uploads = new List + { + new() + { + Content = stream, + ContentType = + FileUploadContentType.FromContentType(HttpConstants.ContentTypes.ImageJpegWithCharset), + Size = content.Length, + Filename = "afilename.jpg" + } + }; + + var result = + _service.GetUploadedFile(uploads, 1000, new List { HttpConstants.ContentTypes.ImageJpeg }); + + result.Should().BeSuccess(); + result.Value.ContentType.Should() + .BeEquivalentTo(FileUploadContentType.FromContentType(HttpConstants.ContentTypes.ImageJpegWithCharset)); result.Value.Filename.Should().Be("afilename.jpg"); result.Value.Size.Should().Be(103L); result.Value.Content.Position.Should().Be(0); @@ -329,7 +362,7 @@ public void WhenGetUploaded_ThenReturns() new() { Content = stream, - ContentType = HttpConstants.ContentTypes.ImageJpeg, + ContentType = FileUploadContentType.FromContentType(HttpConstants.ContentTypes.ImageJpeg), Size = content.Length, Filename = "afilename.jpg" } @@ -339,7 +372,8 @@ public void WhenGetUploaded_ThenReturns() _service.GetUploadedFile(uploads, 1000, new List { HttpConstants.ContentTypes.ImageJpeg }); result.Should().BeSuccess(); - result.Value.ContentType.Should().Be(HttpConstants.ContentTypes.ImageJpeg); + result.Value.ContentType.Should() + .BeEquivalentTo(FileUploadContentType.FromContentType(HttpConstants.ContentTypes.ImageJpeg)); result.Value.Filename.Should().Be("afilename.jpg"); result.Value.Size.Should().Be(103L); result.Value.Content.Position.Should().Be(0); diff --git a/src/Infrastructure.Web.Api.Common/Extensions/HttpRequestExtensions.cs b/src/Infrastructure.Web.Api.Common/Extensions/HttpRequestExtensions.cs index ab98f1e8..124b0a02 100644 --- a/src/Infrastructure.Web.Api.Common/Extensions/HttpRequestExtensions.cs +++ b/src/Infrastructure.Web.Api.Common/Extensions/HttpRequestExtensions.cs @@ -145,7 +145,7 @@ public static Result GetUploadedFile(this HttpRequest request .Select(file => new FileUpload { Content = file.OpenReadStream(), - ContentType = file.ContentType, + ContentType = FileUploadContentType.FromContentType(file.ContentType), Filename = file.FileName, Size = file.Length }).ToList(); @@ -158,17 +158,7 @@ public static Result GetUploadedFile(this HttpRequest request /// public static bool IsContentType(this HttpRequest request, string contentType) { - if (contentType.HasNoValue()) - { - return false; - } - - if (!MediaTypeHeaderValue.TryParse(request.ContentType, out var mediaType)) - { - return false; - } - - return mediaType.MediaType.EqualsIgnoreCase(contentType); + return contentType.IsMediaType(request.ContentType); } /// @@ -297,4 +287,24 @@ public static async Task VerifyHMACSignatureAsync(this HttpRequest request return verifier.Verify(signature); } + + private static bool IsMediaType(this string? source, string? target) + { + if (source.HasNoValue() || target.HasNoValue()) + { + return false; + } + + if (!MediaTypeHeaderValue.TryParse(source, out var sourceMediaType)) + { + return false; + } + + if (!MediaTypeHeaderValue.TryParse(target, out var targetMediaType)) + { + return false; + } + + return sourceMediaType.MediaType.EqualsIgnoreCase(targetMediaType.MediaType); + } } \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Common/FileUploadService.cs b/src/Infrastructure.Web.Api.Common/FileUploadService.cs index 3ece52cc..77175a6d 100644 --- a/src/Infrastructure.Web.Api.Common/FileUploadService.cs +++ b/src/Infrastructure.Web.Api.Common/FileUploadService.cs @@ -10,12 +10,12 @@ namespace Infrastructure.Web.Api.Common; /// public sealed class FileUploadService : IFileUploadService { - internal const string UnknownContentType = "unknown"; + internal const string UnknownMediaType = "unknown"; internal static readonly byte[] ImageJpegMagicBytes = [0xFF, 0xD8, 0xFF]; private static readonly byte[] ImageGif87MagicBytes = [0x47, 0x49, 0x46, 0x38, 0x37, 0x61]; private static readonly byte[] ImageGif89MagicBytes = [0x47, 0x49, 0x46, 0x38, 0x39, 0x61]; private static readonly byte[] ImagePngMagicBytes = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]; - private static readonly IReadOnlyList DetectableFileTypes = new[] + private static readonly IReadOnlyList DetectableImageTypes = new[] { new KnownFile(HttpConstants.ContentTypes.ImageJpeg, ImageJpegMagicBytes, "jpg"), new KnownFile(HttpConstants.ContentTypes.ImageGif, ImageGif87MagicBytes, "gif"), @@ -44,29 +44,30 @@ public Result GetUploadedFile(IReadOnlyList uploa } var content = upload.Content; - var declaredContentType = upload.ContentType; - var detected = DetectFileContent(content, declaredContentType, allowableContentTypes); + var declaredMediaType = upload.ContentType.MediaType; + var detected = DetectFileContent(content, declaredMediaType, allowableContentTypes); if (!detected.IsAllowed) { return Error.Validation(Resources.FileUploadService_DisallowedFileContent - .Format(declaredContentType.HasValue() - ? declaredContentType - : UnknownContentType)); + .Format(declaredMediaType.HasValue() + ? declaredMediaType + : UnknownMediaType)); } - var detectedContentType = detected.ContentType; - if (declaredContentType.HasValue() && detectedContentType.HasValue() - && detectedContentType.NotEqualsIgnoreCase(declaredContentType)) + var detectedMediaType = detected.MediaType; + if (declaredMediaType.HasValue() + && detectedMediaType.HasValue() + && declaredMediaType.NotEqualsIgnoreCase(detectedMediaType)) { return Error.Validation( - Resources.FileUploadService_InvalidContentTypeForFileType.Format(detectedContentType, - declaredContentType)); + Resources.FileUploadService_InvalidContentTypeForFileType.Format(detectedMediaType, + declaredMediaType)); } - if (declaredContentType.HasNoValue() - && detected.ContentType.HasValue()) + if (declaredMediaType.HasNoValue() + && detected.MediaType.HasValue()) { - upload.ContentType = detected.ContentType; + upload.ContentType = FileUploadContentType.FromContentType(detected.MediaType); } var detectedExtension = detected.FileExtension.HasValue() @@ -94,38 +95,37 @@ public Result GetUploadedFile(IReadOnlyList uploa return upload; } - private static (bool IsAllowed, string ContentType, string? FileExtension) DetectFileContent(Stream content, - string declaredContentType, - IReadOnlyList allowableContentTypes) + private static (bool IsAllowed, string? MediaType, string? FileExtension) DetectFileContent(Stream content, + string? declaredMediaType, IReadOnlyList allowableMediaTypes) { - if (allowableContentTypes.HasNone()) + if (allowableMediaTypes.HasNone()) { - return (false, declaredContentType, null); + return (false, declaredMediaType, null); } - foreach (var detectableFileType in DetectableFileTypes) + foreach (var detectableFileType in DetectableImageTypes) { - if (IsContentType(content, detectableFileType.MagicBytes)) + if (IsMatchingStream(content, detectableFileType.MagicBytes)) { - if (allowableContentTypes.Contains(detectableFileType.ContentType)) + if (allowableMediaTypes.Contains(detectableFileType.MediaType)) { - return (true, detectableFileType.ContentType, detectableFileType.FileExtension); + return (true, detectableFileType.MediaType, detectableFileType.FileExtension); } } } - if (declaredContentType.HasValue() - && allowableContentTypes.Contains(declaredContentType)) + if (declaredMediaType.HasValue() + && allowableMediaTypes.Contains(declaredMediaType)) { - return (true, declaredContentType, null); + return (true, declaredMediaType, null); } return (false, HttpConstants.ContentTypes.OctetStream, null); } - private static bool IsContentType(Stream content, byte[] magicBytes) + private static bool IsMatchingStream(Stream content, byte[] magicBytes) { - var maxBufferSize = DetectableFileTypes.Max(kf => kf.MagicBytes.Length); + var maxBufferSize = DetectableImageTypes.Max(kf => kf.MagicBytes.Length); var buffer = new byte[maxBufferSize]; int bytesRead; try @@ -144,5 +144,5 @@ private static bool IsContentType(Stream content, byte[] magicBytes) return isMatch; } - private record KnownFile(string ContentType, byte[] MagicBytes, string FileExtension); + private record KnownFile(string MediaType, byte[] MagicBytes, string FileExtension); } \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Interfaces/HttpConstants.cs b/src/Infrastructure.Web.Api.Interfaces/HttpConstants.cs index 5455db8f..d8ce5289 100644 --- a/src/Infrastructure.Web.Api.Interfaces/HttpConstants.cs +++ b/src/Infrastructure.Web.Api.Interfaces/HttpConstants.cs @@ -16,6 +16,7 @@ public static class ContentTypes public const string Html = "text/html"; public const string ImageGif = "image/gif"; public const string ImageJpeg = "image/jpeg"; + public const string ImageJpegWithCharset = "image/jpeg; utf-8"; public const string ImagePng = "image/png"; public const string Json = "application/json"; public const string JsonProblem = "application/problem+json"; diff --git a/src/Infrastructure.Web.Api.Operations.Shared/Identities/GetRegistrationPersonConfirmationRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/Identities/GetRegistrationPersonConfirmationRequest.cs index de6a5099..ffc0d0fd 100644 --- a/src/Infrastructure.Web.Api.Operations.Shared/Identities/GetRegistrationPersonConfirmationRequest.cs +++ b/src/Infrastructure.Web.Api.Operations.Shared/Identities/GetRegistrationPersonConfirmationRequest.cs @@ -3,7 +3,7 @@ namespace Infrastructure.Web.Api.Operations.Shared.Identities; -[Route("/passwords/confirmation/token", OperationMethod.Get, isTestingOnly: true)] +[Route("/passwords/confirm-registration", OperationMethod.Get, isTestingOnly: true)] public class GetRegistrationPersonConfirmationRequest : UnTenantedRequest { public required string UserId { get; set; } diff --git a/src/OrganizationsApplication.UnitTests/OrganizationsApplicationSpec.cs b/src/OrganizationsApplication.UnitTests/OrganizationsApplicationSpec.cs index 41e9e551..7f8e0230 100644 --- a/src/OrganizationsApplication.UnitTests/OrganizationsApplicationSpec.cs +++ b/src/OrganizationsApplication.UnitTests/OrganizationsApplicationSpec.cs @@ -430,7 +430,7 @@ public async Task WhenChangeAvatarAsyncAndNotExists_ThenReturnsError() var upload = new FileUpload { Content = new MemoryStream(), - ContentType = "acontenttype" + ContentType = new FileUploadContentType { MediaType = "acontenttype" } }; var result = @@ -452,7 +452,7 @@ public async Task WhenChangeAvatarAsyncAndNoExistingAvatar_ThenReturnsOrganizati var upload = new FileUpload { Content = new MemoryStream(), - ContentType = "acontenttype" + ContentType = new FileUploadContentType { MediaType = "acontenttype" } }; _imagesService.Setup(isv => isv.CreateImageAsync(It.IsAny(), It.IsAny(), It.IsAny(), @@ -498,7 +498,7 @@ await org.ChangeAvatarAsync("auserid".ToId(), Roles.Create(TenantRoles.Owner).Va var upload = new FileUpload { Content = new MemoryStream(), - ContentType = "acontenttype" + ContentType = new FileUploadContentType { MediaType = "acontenttype" } }; _imagesService.Setup(isv => isv.CreateImageAsync(It.IsAny(), It.IsAny(), It.IsAny(), diff --git a/src/UserProfilesApplication.UnitTests/UserProfileApplication.DomainEventHandlersSpec.cs b/src/UserProfilesApplication.UnitTests/UserProfileApplication.DomainEventHandlersSpec.cs index d4faa65c..59b537f7 100644 --- a/src/UserProfilesApplication.UnitTests/UserProfileApplication.DomainEventHandlersSpec.cs +++ b/src/UserProfilesApplication.UnitTests/UserProfileApplication.DomainEventHandlersSpec.cs @@ -164,7 +164,7 @@ public async Task WhenHandleEndUserRegisteredAsyncForPersonAndHasDefaultAvatar_T var upload = new FileUpload { Content = new MemoryStream(), - ContentType = "acontenttype", + ContentType = new FileUploadContentType { MediaType = "acontenttype" }, Filename = null, Size = 0 }; diff --git a/src/UserProfilesApplication.UnitTests/UserProfileApplicationSpec.cs b/src/UserProfilesApplication.UnitTests/UserProfileApplicationSpec.cs index bcffb459..bb2176b0 100644 --- a/src/UserProfilesApplication.UnitTests/UserProfileApplicationSpec.cs +++ b/src/UserProfilesApplication.UnitTests/UserProfileApplicationSpec.cs @@ -285,7 +285,7 @@ public async Task WhenChangeProfileAvatarAsyncAndNotExists_ThenReturnsError() var upload = new FileUpload { Content = new MemoryStream(), - ContentType = "acontenttype", + ContentType = new FileUploadContentType { MediaType = "acontenttype" }, Filename = null, Size = 0 }; @@ -304,7 +304,7 @@ public async Task WhenChangeProfileAvatarAsyncAndNotOwner_ThenReturnsError() var upload = new FileUpload { Content = new MemoryStream(), - ContentType = "acontenttype", + ContentType = new FileUploadContentType { MediaType = "acontenttype" }, Filename = null, Size = 0 }; @@ -327,7 +327,7 @@ public async Task WhenChangeProfileAvatarAsyncAndNoExistingAvatar_ThenReturnsPro var upload = new FileUpload { Content = new MemoryStream(), - ContentType = "acontenttype", + ContentType = new FileUploadContentType { MediaType = "acontenttype" }, Filename = null, Size = 0 }; @@ -371,7 +371,7 @@ public async Task WhenChangeProfileAvatarAsyncAndExistingAvatar_ThenReturnsProfi var upload = new FileUpload { Content = new MemoryStream(), - ContentType = "acontenttype", + ContentType = new FileUploadContentType { MediaType = "acontenttype" }, Filename = null, Size = 0 }; @@ -494,8 +494,8 @@ public async Task WhenGetCurrentUserProfileAsyncAndAuthenticated_ThenReturnsProf { _caller.Setup(cc => cc.IsAuthenticated) .Returns(true); - var roles = new ICallerContext.CallerRoles(new[] { PlatformRoles.Standard }, Array.Empty()); - var features = new ICallerContext.CallerFeatures(new[] { PlatformFeatures.Basic }, Array.Empty()); + var roles = new ICallerContext.CallerRoles(new[] { PlatformRoles.Standard }, []); + var features = new ICallerContext.CallerFeatures(new[] { PlatformFeatures.Basic }, []); _caller.Setup(cc => cc.Roles) .Returns(roles); _caller.Setup(cc => cc.Features)