diff --git a/src/ApplicationCore/Interfaces/IFileSystem.cs b/src/ApplicationCore/Interfaces/IFileSystem.cs deleted file mode 100644 index 29d9f04..0000000 --- a/src/ApplicationCore/Interfaces/IFileSystem.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.eShopWeb.ApplicationCore.Interfaces -{ - public interface IFileSystem - { - Task SavePicture(string pictureName, string pictureBase64, CancellationToken cancellationToken); - } -} diff --git a/src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs b/src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs index f3d0954..d6d577a 100644 --- a/src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs +++ b/src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs @@ -7,11 +7,11 @@ namespace Microsoft.eShopWeb.ApplicationCore.Specifications { public CatalogFilterPaginatedSpecification(int skip, int take, int? brandId, int? typeId) : base() - { + { Query .Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) && (!typeId.HasValue || i.CatalogTypeId == typeId)) - .Paginate(skip, take); + .Skip(skip).Take(take); } } } diff --git a/src/BlazorAdmin/Pages/CatalogItemPage/Create.razor b/src/BlazorAdmin/Pages/CatalogItemPage/Create.razor index 07f2b71..0101526 100644 --- a/src/BlazorAdmin/Pages/CatalogItemPage/Create.razor +++ b/src/BlazorAdmin/Pages/CatalogItemPage/Create.razor @@ -80,23 +80,7 @@ - - -
- -
-
- -
-
- @if (HasPicture) - { - - } -
- @_badFileMessage -
-
+ @@ -171,26 +155,4 @@ _modalClass = ""; _showCreateModal = false; } - - private async Task AddFile(IFileListEntry[] files) - { - _badFileMessage = string.Empty; - - var file = files.FirstOrDefault(); - _item.PictureName = file?.Name; - _item.PictureBase64 = await CatalogItem.DataToBase64(file); - - _badFileMessage = CatalogItem.IsValidImage(_item.PictureName, _item.PictureBase64); - if (!string.IsNullOrEmpty(_badFileMessage)) - { - _item.PictureName = null; - _item.PictureBase64 = null; - } - } - - private void RemoveImage() - { - _item.PictureName = null; - _item.PictureBase64 = null; - } } diff --git a/src/BlazorAdmin/Pages/CatalogItemPage/Edit.razor b/src/BlazorAdmin/Pages/CatalogItemPage/Edit.razor index 415765f..9ae161c 100644 --- a/src/BlazorAdmin/Pages/CatalogItemPage/Edit.razor +++ b/src/BlazorAdmin/Pages/CatalogItemPage/Edit.razor @@ -83,23 +83,7 @@ - - -
- -
-
- -
-
- @if (HasPicture) - { - - } -
- @_badFileMessage -
-
+ @@ -170,27 +154,4 @@ _modalClass = ""; _showEditModal = false; } - - private async Task ChangeFile(IFileListEntry[] files) - { - _badFileMessage = string.Empty; - - var file = files.FirstOrDefault(); - _item.PictureName = file?.Name; - _item.PictureBase64 = await CatalogItem.DataToBase64(file); - - _badFileMessage = CatalogItem.IsValidImage(_item.PictureName, _item.PictureBase64); - if (!string.IsNullOrEmpty(_badFileMessage)) - { - _item.PictureName = null; - _item.PictureBase64 = null; - } - } - - private void RemoveImage() - { - _item.PictureName = null; - _item.PictureBase64 = null; - _item.PictureUri = null; - } } diff --git a/src/Infrastructure/Services/WebFileSystem.cs b/src/Infrastructure/Services/WebFileSystem.cs deleted file mode 100644 index b51ba54..0000000 --- a/src/Infrastructure/Services/WebFileSystem.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Microsoft.eShopWeb.ApplicationCore.Interfaces; -using Microsoft.eShopWeb.Infrastructure.Data; -using System; -using System.IO; -using System.Net.Http; -using System.Text; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.eShopWeb.Infrastructure.Services -{ - public class WebFileSystem : IFileSystem - { - private readonly HttpClient _httpClient; - private readonly string _url; - public const string AUTH_KEY = "AuthKeyOfDoomThatMustBeAMinimumNumberOfBytes"; - - public WebFileSystem(string url) - { - _url = url; - _httpClient = new HttpClient(); - _httpClient.DefaultRequestHeaders.Add("auth-key", AUTH_KEY); - } - - public async Task SavePicture(string pictureName, string pictureBase64, CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(pictureBase64) || !await UploadFile(pictureName, Convert.FromBase64String(pictureBase64), cancellationToken)) - { - return false; - } - - return true; - } - - private async Task UploadFile(string fileName, byte[] fileData, CancellationToken cancellationToken) - { - if (!fileData.IsValidImage(fileName)) - { - return false; - } - - return await UploadToWeb(fileName, fileData, cancellationToken); - } - - private async Task UploadToWeb(string fileName, byte[] fileData, CancellationToken cancellationToken) - { - var request = new FileItem - { - DataBase64 = Convert.ToBase64String(fileData), - FileName = fileName - }; - var content = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); - - using var message = await _httpClient.PostAsync(_url, content, cancellationToken); - if (!message.IsSuccessStatusCode) - { - return false; - } - - return true; - } - } - - public static class ImageValidators - { - private const int ImageMaximumBytes = 512000; - - public static bool IsValidImage(this byte[] postedFile, string fileName) - { - return postedFile != null && postedFile.Length > 0 && postedFile.Length <= ImageMaximumBytes && IsExtensionValid(fileName); - } - - private static bool IsExtensionValid(string fileName) - { - var extension = Path.GetExtension(fileName); - - return string.Equals(extension, ".jpg", StringComparison.OrdinalIgnoreCase) || - string.Equals(extension, ".png", StringComparison.OrdinalIgnoreCase) || - string.Equals(extension, ".gif", StringComparison.OrdinalIgnoreCase) || - string.Equals(extension, ".jpeg", StringComparison.OrdinalIgnoreCase); - } - } -} diff --git a/src/PublicApi/CatalogItemEndpoints/Create.cs b/src/PublicApi/CatalogItemEndpoints/Create.cs index ef35707..1cb5eee 100644 --- a/src/PublicApi/CatalogItemEndpoints/Create.cs +++ b/src/PublicApi/CatalogItemEndpoints/Create.cs @@ -19,13 +19,11 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints { private readonly IAsyncRepository _itemRepository; private readonly IUriComposer _uriComposer; - private readonly IFileSystem _webFileSystem; - public Create(IAsyncRepository itemRepository, IUriComposer uriComposer, IFileSystem webFileSystem) + public Create(IAsyncRepository itemRepository, IUriComposer uriComposer) { _itemRepository = itemRepository; _uriComposer = uriComposer; - _webFileSystem = webFileSystem; } [HttpPost("api/catalog-items")] @@ -45,12 +43,12 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints if (newItem.Id != 0) { - var picName = $"{newItem.Id}{Path.GetExtension(request.PictureName)}"; - if (await _webFileSystem.SavePicture(picName, request.PictureBase64, cancellationToken)) - { - newItem.UpdatePictureUri(picName); - await _itemRepository.UpdateAsync(newItem, cancellationToken); - } + //We disabled the upload functionality and added a default/placeholder image to this sample due to a potential security risk + // pointed out by the community. More info in this issue: https://github.com/dotnet-architecture/eShopOnWeb/issues/537 + // In production, we recommend uploading to a blob storage and deliver the image via CDN after a verification process. + + newItem.UpdatePictureUri("eCatalog-item-default.png"); + await _itemRepository.UpdateAsync(newItem, cancellationToken); } var dto = new CatalogItemDto diff --git a/src/PublicApi/CatalogItemEndpoints/Update.cs b/src/PublicApi/CatalogItemEndpoints/Update.cs index 991c72e..97b45a4 100644 --- a/src/PublicApi/CatalogItemEndpoints/Update.cs +++ b/src/PublicApi/CatalogItemEndpoints/Update.cs @@ -17,15 +17,12 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints .WithResponse { private readonly IAsyncRepository _itemRepository; - private readonly IUriComposer _uriComposer; - private readonly IFileSystem _webFileSystem; + private readonly IUriComposer _uriComposer; - public Update(IAsyncRepository itemRepository, IUriComposer uriComposer, IFileSystem webFileSystem) + public Update(IAsyncRepository itemRepository, IUriComposer uriComposer) { _itemRepository = itemRepository; - _uriComposer = uriComposer; - _webFileSystem = webFileSystem; - + _uriComposer = uriComposer; } [HttpPut("api/catalog-items")] @@ -43,20 +40,7 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints existingItem.UpdateDetails(request.Name, request.Description, request.Price); existingItem.UpdateBrand(request.CatalogBrandId); - existingItem.UpdateType(request.CatalogTypeId); - - if (string.IsNullOrEmpty(request.PictureBase64) && string.IsNullOrEmpty(request.PictureUri)) - { - existingItem.UpdatePictureUri(string.Empty); - } - else - { - var picName = $"{existingItem.Id}{Path.GetExtension(request.PictureName)}"; - if (await _webFileSystem.SavePicture($"{picName}", request.PictureBase64, cancellationToken)) - { - existingItem.UpdatePictureUri(picName); - } - } + existingItem.UpdateType(request.CatalogTypeId); await _itemRepository.UpdateAsync(existingItem, cancellationToken); diff --git a/src/PublicApi/Startup.cs b/src/PublicApi/Startup.cs index e2d4e19..0747537 100644 --- a/src/PublicApi/Startup.cs +++ b/src/PublicApi/Startup.cs @@ -95,8 +95,7 @@ namespace Microsoft.eShopWeb.PublicApi services.AddScoped(); var baseUrlConfig = new BaseUrlConfiguration(); - Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig); - services.AddScoped(x => new WebFileSystem($"{baseUrlConfig.WebBase}File")); + Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig); services.AddMemoryCache(); diff --git a/src/Web/Controllers/FileController.cs b/src/Web/Controllers/FileController.cs deleted file mode 100644 index 1e3b9c3..0000000 --- a/src/Web/Controllers/FileController.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopWeb.Web.ViewModels.File; -using System; -using System.IO; - -namespace Microsoft.eShopWeb.Web.Controllers -{ - [Route("[controller]")] - [ApiController] - public class FileController : ControllerBase - { - [HttpPost] - [AllowAnonymous] - public IActionResult Upload(FileViewModel fileViewModel) - { - if (!Request.Headers.ContainsKey("auth-key") || Request.Headers["auth-key"].ToString() != ApplicationCore.Constants.AuthorizationConstants.AUTH_KEY) - { - return Unauthorized(); - } - - if(fileViewModel == null || string.IsNullOrEmpty(fileViewModel.DataBase64)) return BadRequest(); - - var fileData = Convert.FromBase64String(fileViewModel.DataBase64); - if (fileData.Length <= 0) return BadRequest(); - - var fullPath = Path.Combine(Directory.GetCurrentDirectory(), @"wwwroot/images/products", fileViewModel.FileName); - if (System.IO.File.Exists(fullPath)) - { - System.IO.File.Delete(fullPath); - } - System.IO.File.WriteAllBytes(fullPath, fileData); - - return Ok(); - } - - } -} \ No newline at end of file diff --git a/src/Web/wwwroot/images/products/eCatalog-item-default.png b/src/Web/wwwroot/images/products/eCatalog-item-default.png new file mode 100644 index 0000000..5079b8b Binary files /dev/null and b/src/Web/wwwroot/images/products/eCatalog-item-default.png differ diff --git a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs index 55d33d4..aff2473 100644 --- a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs +++ b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs @@ -18,8 +18,7 @@ namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers private int _testBrandId = 1; private int _testTypeId = 2; private string _testDescription = "test description"; - private string _testName = "test name"; - private string _testUri = "test uri"; + private string _testName = "test name"; private decimal _testPrice = 1.23m; public CreateEndpoint(ApiTestFixture factory) @@ -54,8 +53,7 @@ namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers Assert.Equal(_testBrandId, model.CatalogItem.CatalogBrandId); Assert.Equal(_testTypeId, model.CatalogItem.CatalogTypeId); Assert.Equal(_testDescription, model.CatalogItem.Description); - Assert.Equal(_testName, model.CatalogItem.Name); - Assert.Equal(_testUri, model.CatalogItem.PictureUri); + Assert.Equal(_testName, model.CatalogItem.Name); Assert.Equal(_testPrice, model.CatalogItem.Price); } @@ -66,8 +64,7 @@ namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers CatalogBrandId = _testBrandId, CatalogTypeId = _testTypeId, Description = _testDescription, - Name = _testName, - PictureUri = _testUri, + Name = _testName, Price = _testPrice }; var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");