From 124383f5ad35fba5d50e88f314768ae07f18ba5c Mon Sep 17 00:00:00 2001 From: Behdad Kardgar Date: Sat, 2 Mar 2024 13:54:24 +0100 Subject: [PATCH 1/5] add more logs --- src/BuildingBlocks/Logging/LoggingExtension.cs | 5 +++-- .../Basket/Basket.API/Controllers/BasketController.cs | 8 +++++++- src/Services/Basket/Basket.API/Grpc/BasketService.cs | 4 ++++ .../Catalog/Catalog.API/Controllers/CatalogController.cs | 7 +++++++ src/Services/Catalog/Catalog.API/Grpc/CatalogService.cs | 8 ++++++++ .../Discount.API/Controllers/DiscountController.cs | 8 +++++++- .../Discount/Discount.API/Grpc/DiscountService.cs | 2 ++ .../Ordering/Ordering.API/Controllers/OrderController.cs | 8 +++++++- src/WebApps/WebMVC/Startup.cs | 2 +- 9 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/BuildingBlocks/Logging/LoggingExtension.cs b/src/BuildingBlocks/Logging/LoggingExtension.cs index 87f747b..0b2c012 100644 --- a/src/BuildingBlocks/Logging/LoggingExtension.cs +++ b/src/BuildingBlocks/Logging/LoggingExtension.cs @@ -97,8 +97,9 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) private static string GetGrafanaUrl(string traceId) { - var url = HttpUtility.UrlEncode("http://localhost:3000/explore?schemaVersion=1&panes={\"vvs\":{\"queries\":[{\"query\":" + traceId + "}]} }"); - return url; + var baseUrl = "http://localhost:3000/explore?schemaVersion=1&panes="; + var query = HttpUtility.UrlEncode("{\"vvs\":{\"queries\":[{\"query\":" + traceId + "}]}}"); + return baseUrl + query; } } diff --git a/src/Services/Basket/Basket.API/Controllers/BasketController.cs b/src/Services/Basket/Basket.API/Controllers/BasketController.cs index 336b749..6853ec4 100644 --- a/src/Services/Basket/Basket.API/Controllers/BasketController.cs +++ b/src/Services/Basket/Basket.API/Controllers/BasketController.cs @@ -14,12 +14,15 @@ public class BasketController : ControllerBase private readonly IBasketRepository _repository; private readonly IRabbitMQProducer _rabbitMQProducer; private readonly DiscountProtoService.DiscountProtoServiceClient _discountProtoServiceClient; + private readonly ILogger _logger; public BasketController( + ILogger logger, IBasketRepository repository, DiscountProtoService.DiscountProtoServiceClient discountProtoServiceClient, IRabbitMQProducer rabbitMQProducer) { + _logger = logger; _repository = repository ?? throw new ArgumentNullException(nameof(repository)); _discountProtoServiceClient = discountProtoServiceClient ?? throw new ArgumentNullException(nameof(discountProtoServiceClient)); _rabbitMQProducer = rabbitMQProducer ?? throw new ArgumentNullException(nameof(rabbitMQProducer)); @@ -32,6 +35,7 @@ public async Task> GetBasket(string username) if (string.IsNullOrEmpty(username)) return BadRequest("Username cannot be null or empty."); + _logger.LogInformation($"Getting basket for username {username}"); var basket = await _repository.GetBasketAsync(username); return Ok(basket ?? new ShoppingCart(username)); } @@ -40,6 +44,7 @@ public async Task> GetBasket(string username) [ProducesResponseType(typeof(ShoppingCart), (int)HttpStatusCode.OK)] public async Task> UpdateBasket([FromBody] ShoppingCart basket) { + _logger.LogInformation($"Updating basket for username {basket.Username}"); foreach (var item in basket.Items) { var discountResquest = new GetDiscountRequest() { ProductName = item.ProductName }; @@ -54,6 +59,7 @@ public async Task> UpdateBasket([FromBody] ShoppingCa [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] public async Task DeleteBasket(string username) { + _logger.LogInformation($"Delete basket for username {username}"); await _repository.DeleteBasketAsync(username); return Ok(); } @@ -68,9 +74,9 @@ public async Task Checkout([FromBody] BasketCheckout basketChecko if (basket == null) return BadRequest(); + _logger.LogInformation($"checking out basket for username {basketCheckout.Username}"); basketCheckout.TotalPrice = basket.TotalPrice; await _rabbitMQProducer.PublishAsJsonAsync("order.checkout", basketCheckout); - await _repository.DeleteBasketAsync(basket.Username); return Accepted(); diff --git a/src/Services/Basket/Basket.API/Grpc/BasketService.cs b/src/Services/Basket/Basket.API/Grpc/BasketService.cs index 460a0f9..5f2ae23 100644 --- a/src/Services/Basket/Basket.API/Grpc/BasketService.cs +++ b/src/Services/Basket/Basket.API/Grpc/BasketService.cs @@ -35,6 +35,7 @@ public override async Task GetBasket(GetBasketRequest request if (string.IsNullOrEmpty(request.Username)) throw new RpcException(new Status(StatusCode.InvalidArgument, "Username cannot be null or empty.")); + _logger.LogInformation($"Getting basket for username {request.Username}"); var basket = await _repository.GetBasketAsync(request.Username); basket ??= new ShoppingCart(request.Username); _logger.LogInformation("Basket is retrieved for Username : {Username}", request.Username); @@ -45,6 +46,7 @@ public override async Task GetBasket(GetBasketRequest request public override async Task UpdateBasket(UpdateBasketRequest request, ServerCallContext context) { + _logger.LogInformation($"Updating basket for username {request.ShoppingCart.Username}"); var ShoppingCartItems = request.ShoppingCart.Items.ToList(); foreach (var item in ShoppingCartItems) @@ -62,6 +64,7 @@ public override async Task UpdateBasket(UpdateBasketRequest r public override async Task DeletetBasket(DeletetBasketRequest request, ServerCallContext context) { + _logger.LogInformation($"Delete basket for username {request.Username}"); await _repository.DeleteBasketAsync(request.Username); return new NoResponse(); } @@ -72,6 +75,7 @@ public override async Task Checkout(CheckoutViewModel request, Serve if (basket == null) throw new RpcException(new Status(StatusCode.NotFound, "Basket not found.")); + _logger.LogInformation($"checking out basket for username {request.Username}"); request.TotalPrice = (double)basket.TotalPrice; var basketCheckout = _mapper.Map(request); diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 7401f40..597b882 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -22,6 +22,7 @@ public CatalogController(IProductRepository repository, ILogger), (int)HttpStatusCode.OK)] public async Task>> GetProducts() { + _logger.LogInformation("Getting the list of all the products."); var products = await _repository.GetProductsAsync(); return Ok(products); } @@ -34,6 +35,8 @@ public async Task> GetProductById(string id) if (string.IsNullOrEmpty(id)) return BadRequest("Product Id cannot be null or empty."); + _logger.LogInformation("Getting product with Id={ProductId}.", id); + var product = await _repository.GetProductAsync(id); if (product == null) { @@ -51,6 +54,7 @@ public async Task>> GetProductByCategory(strin if (string.IsNullOrEmpty(category)) return BadRequest("Category cannot be null or empty."); + _logger.LogInformation("Getting the list of all the products for Category={category}", category); var products = await _repository.GetProductByCategoryAsync(category); return Ok(products); } @@ -59,6 +63,7 @@ public async Task>> GetProductByCategory(strin [ProducesResponseType(typeof(Product), (int)HttpStatusCode.Created)] public async Task> CreateProduct([FromBody] Product product) { + _logger.LogInformation($"Cearting product: {product}"); await _repository.CreateProductAsync(product); return CreatedAtRoute("GetProduct", new { id = product.Id }, product); } @@ -67,6 +72,7 @@ public async Task> CreateProduct([FromBody] Product produc [ProducesResponseType(typeof(Product), (int)HttpStatusCode.OK)] public async Task UpdateProduct([FromBody] Product product) { + _logger.LogInformation($"Updating product {product}"); return Ok(await _repository.UpdateProductAsync(product)); } @@ -77,6 +83,7 @@ public async Task DeleteProductById(string id) if (string.IsNullOrEmpty(id)) return BadRequest("Product Id cannot be null or empty."); + _logger.LogInformation($"Deleting product with Id {id}"); return Ok(await _repository.DeleteProductAsync(id)); } diff --git a/src/Services/Catalog/Catalog.API/Grpc/CatalogService.cs b/src/Services/Catalog/Catalog.API/Grpc/CatalogService.cs index 79f0262..46bd32d 100644 --- a/src/Services/Catalog/Catalog.API/Grpc/CatalogService.cs +++ b/src/Services/Catalog/Catalog.API/Grpc/CatalogService.cs @@ -21,6 +21,7 @@ public CatalogService(IProductRepository repository, ILogger log public override async Task GetProducts(NoRequest request, ServerCallContext context) { + _logger.LogInformation("Getting the list of all the products."); var products = await _repository.GetProductsAsync(); var productModels = _mapper.Map>(products); var result = new GetProductsResponse(); @@ -30,6 +31,8 @@ public override async Task GetProducts(NoRequest request, S public override async Task GetProductById(GetProductByIdRequest request, ServerCallContext context) { + _logger.LogInformation("Getting product with Id={ProductId}.", request.Id); + var product = await _repository.GetProductAsync(request.Id); if (product == null) @@ -41,6 +44,8 @@ public override async Task GetProductById(GetProductByIdRequest re public override async Task GetProductByCategory(GetProductByCategoryRequest request, ServerCallContext context) { + _logger.LogInformation("Getting the list of all the products for Category={category}", request.Category); + var products = await _repository.GetProductByCategoryAsync(request.Category); var productModels = _mapper.Map>(products); var result = new GetProductByCategoryResponse(); @@ -50,6 +55,7 @@ public override async Task GetProductByCategory(Ge public override async Task CreateProduct(CreateProductRequest request, ServerCallContext context) { + _logger.LogInformation($"Cearting product: {request.Product}"); var product = _mapper.Map(request.Product); await _repository.CreateProductAsync(product); @@ -59,6 +65,7 @@ public override async Task CreateProduct(CreateProductRequest requ public override async Task UpdateProduct(UpdateProductRequest request, ServerCallContext context) { + _logger.LogInformation($"Updating product {request.Product}"); var product = _mapper.Map(request.Product); var result = await _repository.UpdateProductAsync(product); return new UpdateProductResponse() { Result = result }; @@ -66,6 +73,7 @@ public override async Task UpdateProduct(UpdateProductReq public override async Task DeleteProductById(DeleteProductIdRequest request, ServerCallContext context) { + _logger.LogInformation($"Deleting product with Id {request.Id}"); var result = await _repository.DeleteProductAsync(request.Id); return new DeleteProductIdResponse() { Result = result }; } diff --git a/src/Services/Discount/Discount.API/Controllers/DiscountController.cs b/src/Services/Discount/Discount.API/Controllers/DiscountController.cs index 7899834..2e67828 100644 --- a/src/Services/Discount/Discount.API/Controllers/DiscountController.cs +++ b/src/Services/Discount/Discount.API/Controllers/DiscountController.cs @@ -10,16 +10,19 @@ namespace Discount.API.Controllers public class DiscountController : ControllerBase { private readonly IDiscountRepository _repository; + private readonly ILogger _logger; - public DiscountController(IDiscountRepository repository) + public DiscountController(IDiscountRepository repository, ILogger logger) { _repository = repository ?? throw new ArgumentNullException(nameof(repository)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } [HttpGet("{productName}", Name = "GetDiscount")] [ProducesResponseType(typeof(Coupon), (int)HttpStatusCode.OK)] public async Task> GetDiscount(string productName) { + _logger.LogInformation($"Getting discount for the prodcut {productName}"); var coupon = await _repository.GetDiscountAsync(productName); return Ok(coupon); } @@ -28,6 +31,7 @@ public async Task> GetDiscount(string productName) [ProducesResponseType(typeof(Coupon), (int)HttpStatusCode.OK)] public async Task> CreateDiscount([FromBody] Coupon coupon) { + _logger.LogInformation($"Create discount for the prodcut {coupon.ProductName}"); await _repository.CreateDiscountAsync(coupon); return CreatedAtRoute("GetDiscount", new { productName = coupon.ProductName }, coupon); } @@ -36,6 +40,7 @@ public async Task> CreateDiscount([FromBody] Coupon coupon) [ProducesResponseType(typeof(Coupon), (int)HttpStatusCode.OK)] public async Task> UpdateDiscount([FromBody] Coupon coupon) { + _logger.LogInformation($"Update discount for the prodcut {coupon.ProductName}"); return Ok(await _repository.UpdateDiscountAsync(coupon)); } @@ -43,6 +48,7 @@ public async Task> UpdateDiscount([FromBody] Coupon coupon) [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] public async Task> DeleteDiscount(string productName) { + _logger.LogInformation($"Delete discount for the prodcut {productName}"); return Ok(await _repository.DeleteDiscountAsync(productName)); } } diff --git a/src/Services/Discount/Discount.API/Grpc/DiscountService.cs b/src/Services/Discount/Discount.API/Grpc/DiscountService.cs index 0800f0e..c04ed81 100644 --- a/src/Services/Discount/Discount.API/Grpc/DiscountService.cs +++ b/src/Services/Discount/Discount.API/Grpc/DiscountService.cs @@ -56,6 +56,8 @@ public override async Task UpdateDiscount(UpdateDiscountRequest req public override async Task DeleteDiscount(DeleteDiscountRequest request, ServerCallContext context) { + _logger.LogInformation($"Delete discount for the prodcut {request.ProductName}"); + var deleted = await _repository.DeleteDiscountAsync(request.ProductName); var response = new DeleteDiscountResponse { diff --git a/src/Services/Ordering/Ordering.API/Controllers/OrderController.cs b/src/Services/Ordering/Ordering.API/Controllers/OrderController.cs index 4cf213d..b1b46da 100644 --- a/src/Services/Ordering/Ordering.API/Controllers/OrderController.cs +++ b/src/Services/Ordering/Ordering.API/Controllers/OrderController.cs @@ -13,16 +13,19 @@ namespace Ordering.API.Controllers public class OrderController : ControllerBase { private readonly IMediator _mediator; + private readonly ILogger _logger; - public OrderController(IMediator mediator) + public OrderController(IMediator mediator, ILogger logger) { _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } [HttpGet("{username}", Name = "GetOrder")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] public async Task>> GetOrdersByUsernameAsync(string username) { + _logger.LogInformation($"Getting order for username {username}"); var query = new GetOrdersListQuery(username); var orders = await _mediator.Send(query); return Ok(orders); @@ -32,6 +35,7 @@ public async Task>> GetOrdersByUsernameAsync( [ProducesResponseType((int)HttpStatusCode.OK)] public async Task> CheckoutOrderAsync([FromBody] CheckoutOrderCommand command) { + _logger.LogInformation($"Checking out orders for username {command.Username}"); var result = await _mediator.Send(command); return Ok(result); } @@ -42,6 +46,7 @@ public async Task> CheckoutOrderAsync([FromBody] CheckoutOrder [ProducesDefaultResponseType] public async Task UpdateOrder([FromBody] UpdateOrderCommand command) { + _logger.LogInformation($"Update orders for username {command.Username}"); await _mediator.Send(command); return NoContent(); } @@ -52,6 +57,7 @@ public async Task UpdateOrder([FromBody] UpdateOrderCommand comman [ProducesDefaultResponseType] public async Task DeleteOrder(int id) { + _logger.LogInformation($"Delete order with id {id}"); var command = new DeleteOrderCommand(id); await _mediator.Send(command); return NoContent(); diff --git a/src/WebApps/WebMVC/Startup.cs b/src/WebApps/WebMVC/Startup.cs index d48eba2..fe6db47 100644 --- a/src/WebApps/WebMVC/Startup.cs +++ b/src/WebApps/WebMVC/Startup.cs @@ -33,7 +33,7 @@ public Startup(IConfiguration configuration) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - const string serviceName = "eshop.webmvc.api"; + const string serviceName = "eshop.webmvc"; var telemetry = new ActivitySource(serviceName); services.AddSingleton(telemetry); var resourceBuilder = ResourceBuilder.CreateDefault().AddService(serviceName); From 19cfdd67d41b9e975edf5bde472be2dcd88de340 Mon Sep 17 00:00:00 2001 From: Behdad Kardgar Date: Sat, 2 Mar 2024 14:14:26 +0100 Subject: [PATCH 2/5] fix unit tests --- .../Basket.API.Tests/BasketControllerTests.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Services/Basket/Basket.API.Tests/BasketControllerTests.cs b/src/Services/Basket/Basket.API.Tests/BasketControllerTests.cs index fbf4cb2..4950f84 100644 --- a/src/Services/Basket/Basket.API.Tests/BasketControllerTests.cs +++ b/src/Services/Basket/Basket.API.Tests/BasketControllerTests.cs @@ -6,6 +6,7 @@ using Eshop.BuildingBlocks.EventBus.RabbitMQ.Abstractions; using Grpc.Core; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Moq; using System; using System.Collections.Generic; @@ -19,6 +20,7 @@ public class BasketControllerTests { private readonly Mock _basketRepositoryMock; private readonly Mock _rabbitMQProducerMock; + private readonly Mock> _logger; private readonly Mock _discountProtoServiceClientMock; public BasketControllerTests() @@ -26,6 +28,7 @@ public BasketControllerTests() _basketRepositoryMock = new Mock(); _rabbitMQProducerMock = new Mock(); _discountProtoServiceClientMock = new Mock(); + _logger = new Mock>(); } [Fact] @@ -33,7 +36,7 @@ public async Task GetBasket_Username_Null_Or_Empty_Return_BadRequest() { // Arrange var username = string.Empty; - var controller = new BasketController(_basketRepositoryMock.Object, _discountProtoServiceClientMock.Object, _rabbitMQProducerMock.Object); + var controller = new BasketController(_logger.Object, _basketRepositoryMock.Object, _discountProtoServiceClientMock.Object, _rabbitMQProducerMock.Object); // Act var actual = await controller.GetBasket(username); @@ -48,7 +51,7 @@ public async Task GetBasket_NoBasket_Return_EmptyBasket() // Arrange var username = "test"; _basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny())).ReturnsAsync(() => null); - var controller = new BasketController(_basketRepositoryMock.Object, _discountProtoServiceClientMock.Object, _rabbitMQProducerMock.Object); + var controller = new BasketController(_logger.Object, _basketRepositoryMock.Object, _discountProtoServiceClientMock.Object, _rabbitMQProducerMock.Object); // Act var actual = await controller.GetBasket(username); @@ -81,7 +84,7 @@ public async Task UpdateBasket_Returns_Updated_Basket() .Returns(discountMockCall); _basketRepositoryMock.Setup(x => x.UpdateBasketAsync(It.IsAny())).ReturnsAsync(basket); - var controller = new BasketController(_basketRepositoryMock.Object, _discountProtoServiceClientMock.Object, _rabbitMQProducerMock.Object); + var controller = new BasketController(_logger.Object, _basketRepositoryMock.Object, _discountProtoServiceClientMock.Object, _rabbitMQProducerMock.Object); // Act var actual = await controller.UpdateBasket(basket); @@ -103,7 +106,7 @@ public async Task Checkout_Return_BadRequest_When_Basket_Is_Null() Username = "test", }; _basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny())).ReturnsAsync(() => null); - var controller = new BasketController(_basketRepositoryMock.Object, _discountProtoServiceClientMock.Object, _rabbitMQProducerMock.Object); + var controller = new BasketController(_logger.Object, _basketRepositoryMock.Object, _discountProtoServiceClientMock.Object, _rabbitMQProducerMock.Object); // Act var actual = await controller.Checkout(basket); @@ -135,7 +138,7 @@ public async Task Checkout_Sends_Out_CheckOut_Event_And_Deletes_Basket() } }); - var controller = new BasketController(_basketRepositoryMock.Object, _discountProtoServiceClientMock.Object, _rabbitMQProducerMock.Object); + var controller = new BasketController(_logger.Object, _basketRepositoryMock.Object, _discountProtoServiceClientMock.Object, _rabbitMQProducerMock.Object); _rabbitMQProducerMock.Setup(x => x.PublishAsJsonAsync(It.IsAny(), It.IsAny())); _basketRepositoryMock.Setup(x => x.DeleteBasketAsync(It.IsAny())); From 02e6c82968719de386d7a43a381affb16eb9bd1c Mon Sep 17 00:00:00 2001 From: Behdad Kardgar Date: Sat, 2 Mar 2024 14:30:44 +0100 Subject: [PATCH 3/5] fix --- .../Basket.API/Controllers/BasketController.cs | 17 ++++++++++++++--- .../Controllers/CatalogController.cs | 4 ++-- src/datasource.yml | 16 ++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/Services/Basket/Basket.API/Controllers/BasketController.cs b/src/Services/Basket/Basket.API/Controllers/BasketController.cs index 6853ec4..12ba59a 100644 --- a/src/Services/Basket/Basket.API/Controllers/BasketController.cs +++ b/src/Services/Basket/Basket.API/Controllers/BasketController.cs @@ -35,7 +35,7 @@ public async Task> GetBasket(string username) if (string.IsNullOrEmpty(username)) return BadRequest("Username cannot be null or empty."); - _logger.LogInformation($"Getting basket for username {username}"); + _logger.LogInformation($"Getting basket for username {GetLogStringValue(username)}"); var basket = await _repository.GetBasketAsync(username); return Ok(basket ?? new ShoppingCart(username)); } @@ -59,7 +59,7 @@ public async Task> UpdateBasket([FromBody] ShoppingCa [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] public async Task DeleteBasket(string username) { - _logger.LogInformation($"Delete basket for username {username}"); + _logger.LogInformation($"Delete basket for username {GetLogStringValue(username)}"); await _repository.DeleteBasketAsync(username); return Ok(); } @@ -74,12 +74,23 @@ public async Task Checkout([FromBody] BasketCheckout basketChecko if (basket == null) return BadRequest(); - _logger.LogInformation($"checking out basket for username {basketCheckout.Username}"); + _logger.LogInformation($"checking out basket for username {GetLogStringValue(basketCheckout.Username)}"); basketCheckout.TotalPrice = basket.TotalPrice; await _rabbitMQProducer.PublishAsJsonAsync("order.checkout", basketCheckout); await _repository.DeleteBasketAsync(basket.Username); return Accepted(); } + + /// + /// Prevents Log Injection attacks + /// https://owasp.org/www-community/attacks/Log_Injection + /// + /// + /// + private static string GetLogStringValue(string value) + { + return value.Replace(Environment.NewLine, ""); + } } } diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 597b882..48e250b 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -35,7 +35,7 @@ public async Task> GetProductById(string id) if (string.IsNullOrEmpty(id)) return BadRequest("Product Id cannot be null or empty."); - _logger.LogInformation("Getting product with Id={ProductId}.", id); + _logger.LogInformation("Getting product with Id={ProductId}.", GetLogStringValue(id)); var product = await _repository.GetProductAsync(id); if (product == null) @@ -83,7 +83,7 @@ public async Task DeleteProductById(string id) if (string.IsNullOrEmpty(id)) return BadRequest("Product Id cannot be null or empty."); - _logger.LogInformation($"Deleting product with Id {id}"); + _logger.LogInformation($"Deleting product with Id {GetLogStringValue(id)}"); return Ok(await _repository.DeleteProductAsync(id)); } diff --git a/src/datasource.yml b/src/datasource.yml index c0bb946..748e0f9 100644 --- a/src/datasource.yml +++ b/src/datasource.yml @@ -14,3 +14,19 @@ datasources: access: proxy uid: P8E80F9AEF21F6940 url: http://loki:3100 + jsonData: + derivedFields: + - datasourceUid: my-jaeger + matcherRegex: ((\d+|[a-z]+)(\d+|[a-z]+)(\d+|[a-z]+)(\d+|[a-z]+)(\d+|[a-z]+)(\d+|[a-z]+)(\d+|[a-z]+)(\d+|[a-z]+)(\d+|[a-z]+)(\d+|[a-z]+)(\d+|[a-z]+)) + url: '$${__value.raw}' + name: TraceID + + - name: jaeger + type: jaeger + uid: my-jaeger + access: proxy + url: http://jaeger:16686 + isDefault: false + version: 1 + editable: true + basicAuth: false From 549f6a02dbed08d464878fd0b8c031479d41ce9b Mon Sep 17 00:00:00 2001 From: Behdad Kardgar Date: Sat, 2 Mar 2024 14:32:05 +0100 Subject: [PATCH 4/5] fix --- .../Basket.API/Controllers/BasketController.cs | 2 +- .../Ordering.API/Controllers/OrderController.cs | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Services/Basket/Basket.API/Controllers/BasketController.cs b/src/Services/Basket/Basket.API/Controllers/BasketController.cs index 12ba59a..aee914a 100644 --- a/src/Services/Basket/Basket.API/Controllers/BasketController.cs +++ b/src/Services/Basket/Basket.API/Controllers/BasketController.cs @@ -44,7 +44,7 @@ public async Task> GetBasket(string username) [ProducesResponseType(typeof(ShoppingCart), (int)HttpStatusCode.OK)] public async Task> UpdateBasket([FromBody] ShoppingCart basket) { - _logger.LogInformation($"Updating basket for username {basket.Username}"); + _logger.LogInformation($"Updating basket for username {GetLogStringValue(basket.Username)}"); foreach (var item in basket.Items) { var discountResquest = new GetDiscountRequest() { ProductName = item.ProductName }; diff --git a/src/Services/Ordering/Ordering.API/Controllers/OrderController.cs b/src/Services/Ordering/Ordering.API/Controllers/OrderController.cs index b1b46da..de54c48 100644 --- a/src/Services/Ordering/Ordering.API/Controllers/OrderController.cs +++ b/src/Services/Ordering/Ordering.API/Controllers/OrderController.cs @@ -25,7 +25,7 @@ public OrderController(IMediator mediator, ILogger logger) [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] public async Task>> GetOrdersByUsernameAsync(string username) { - _logger.LogInformation($"Getting order for username {username}"); + _logger.LogInformation($"Getting order for username {GetLogStringValue(username)}"); var query = new GetOrdersListQuery(username); var orders = await _mediator.Send(query); return Ok(orders); @@ -35,7 +35,7 @@ public async Task>> GetOrdersByUsernameAsync( [ProducesResponseType((int)HttpStatusCode.OK)] public async Task> CheckoutOrderAsync([FromBody] CheckoutOrderCommand command) { - _logger.LogInformation($"Checking out orders for username {command.Username}"); + _logger.LogInformation($"Checking out orders for username {GetLogStringValue(command.Username)}"); var result = await _mediator.Send(command); return Ok(result); } @@ -46,7 +46,7 @@ public async Task> CheckoutOrderAsync([FromBody] CheckoutOrder [ProducesDefaultResponseType] public async Task UpdateOrder([FromBody] UpdateOrderCommand command) { - _logger.LogInformation($"Update orders for username {command.Username}"); + _logger.LogInformation($"Update orders for username {GetLogStringValue(command.Username)}"); await _mediator.Send(command); return NoContent(); } @@ -62,5 +62,16 @@ public async Task DeleteOrder(int id) await _mediator.Send(command); return NoContent(); } + + /// + /// Prevents Log Injection attacks + /// https://owasp.org/www-community/attacks/Log_Injection + /// + /// + /// + private static string GetLogStringValue(string value) + { + return value.Replace(Environment.NewLine, ""); + } } } From 3610fa9212f93c5acc3ca2c193985b5c3b4a5cbb Mon Sep 17 00:00:00 2001 From: Behdad Kardgar Date: Sat, 2 Mar 2024 14:42:18 +0100 Subject: [PATCH 5/5] fix --- .../Controllers/CatalogController.cs | 7 ++++--- .../Controllers/DiscountController.cs | 19 +++++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 48e250b..04a1990 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -1,6 +1,7 @@ using Catalog.API.Entities; using Catalog.API.Repositories; using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; using System.Net; namespace Catalog.API.Controllers @@ -54,7 +55,7 @@ public async Task>> GetProductByCategory(strin if (string.IsNullOrEmpty(category)) return BadRequest("Category cannot be null or empty."); - _logger.LogInformation("Getting the list of all the products for Category={category}", category); + _logger.LogInformation("Getting the list of all the products for Category={category}", GetLogStringValue(category)); var products = await _repository.GetProductByCategoryAsync(category); return Ok(products); } @@ -63,7 +64,7 @@ public async Task>> GetProductByCategory(strin [ProducesResponseType(typeof(Product), (int)HttpStatusCode.Created)] public async Task> CreateProduct([FromBody] Product product) { - _logger.LogInformation($"Cearting product: {product}"); + _logger.LogInformation($"Cearting product: {GetLogStringValue(JsonConvert.SerializeObject(product))}"); await _repository.CreateProductAsync(product); return CreatedAtRoute("GetProduct", new { id = product.Id }, product); } @@ -72,7 +73,7 @@ public async Task> CreateProduct([FromBody] Product produc [ProducesResponseType(typeof(Product), (int)HttpStatusCode.OK)] public async Task UpdateProduct([FromBody] Product product) { - _logger.LogInformation($"Updating product {product}"); + _logger.LogInformation($"Updating product {GetLogStringValue(JsonConvert.SerializeObject(product))}"); return Ok(await _repository.UpdateProductAsync(product)); } diff --git a/src/Services/Discount/Discount.API/Controllers/DiscountController.cs b/src/Services/Discount/Discount.API/Controllers/DiscountController.cs index 2e67828..1ee533f 100644 --- a/src/Services/Discount/Discount.API/Controllers/DiscountController.cs +++ b/src/Services/Discount/Discount.API/Controllers/DiscountController.cs @@ -22,7 +22,7 @@ public DiscountController(IDiscountRepository repository, ILogger> GetDiscount(string productName) { - _logger.LogInformation($"Getting discount for the prodcut {productName}"); + _logger.LogInformation($"Getting discount for the prodcut {GetLogStringValue(productName)}"); var coupon = await _repository.GetDiscountAsync(productName); return Ok(coupon); } @@ -31,7 +31,7 @@ public async Task> GetDiscount(string productName) [ProducesResponseType(typeof(Coupon), (int)HttpStatusCode.OK)] public async Task> CreateDiscount([FromBody] Coupon coupon) { - _logger.LogInformation($"Create discount for the prodcut {coupon.ProductName}"); + _logger.LogInformation($"Create discount for the prodcut {GetLogStringValue(coupon.ProductName)}"); await _repository.CreateDiscountAsync(coupon); return CreatedAtRoute("GetDiscount", new { productName = coupon.ProductName }, coupon); } @@ -40,7 +40,7 @@ public async Task> CreateDiscount([FromBody] Coupon coupon) [ProducesResponseType(typeof(Coupon), (int)HttpStatusCode.OK)] public async Task> UpdateDiscount([FromBody] Coupon coupon) { - _logger.LogInformation($"Update discount for the prodcut {coupon.ProductName}"); + _logger.LogInformation($"Update discount for the prodcut {GetLogStringValue(coupon.ProductName)}"); return Ok(await _repository.UpdateDiscountAsync(coupon)); } @@ -48,8 +48,19 @@ public async Task> UpdateDiscount([FromBody] Coupon coupon) [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] public async Task> DeleteDiscount(string productName) { - _logger.LogInformation($"Delete discount for the prodcut {productName}"); + _logger.LogInformation($"Delete discount for the prodcut {GetLogStringValue(productName)}"); return Ok(await _repository.DeleteDiscountAsync(productName)); } + + /// + /// Prevents Log Injection attacks + /// https://owasp.org/www-community/attacks/Log_Injection + /// + /// + /// + private static string GetLogStringValue(string value) + { + return value.Replace(Environment.NewLine, ""); + } } }