From 32de36f78cef5030ebce607ed75d584f2d3a98d9 Mon Sep 17 00:00:00 2001 From: Alexandre Brandizzi Date: Fri, 24 Jul 2026 14:14:39 -0300 Subject: [PATCH] fix(backend): sanitize exception disclosure in changed API endpoints Replace raw exception-message disclosure (ex.Message) returned to API callers with a stable sanitized public message plus correlated structured internal logging, across the endpoints changed in this PR. - Add SanitizedErrors helper: logs the original exception at Error with a generated correlation id and returns a stable public message referencing it so support can trace without exposing internals. - Inject ILogger into the 14 changed controllers and route every ex.Message/dbex.Message disclosure through the helper, preserving status codes, response shapes, and business data (e.g. OpenWorkOrders). - Leave FluentValidation (vex.Errors) and existing fixed-message catches untouched; out-of-scope controllers (Account/Contact/Employee/Asset/ PMSchedule) are unchanged. - Add focused tests proving internal exception text is not returned and that Error logging carrying the original exception is invoked. --- .../AuthenticationControllerTests.cs | 32 ++++++++- .../CalendarControllerTests.cs | 26 +++++-- .../FollowUpControllerTests.cs | 5 +- .../LocationControllerTests.cs | 3 +- .../SanitizedErrorsTests.cs | 71 +++++++++++++++++++ .../SettingsControllerTests.cs | 3 +- .../UpliftControllerTests.cs | 38 +++++++++- .../UserControllerTests.cs | 3 +- .../VendorControllerTests.cs | 43 ++++++++++- .../VendorPortalControllerTests.cs | 9 ++- .../Api.SeaHavenIndustries.csproj | 4 ++ .../Controllers/AuthenticationController.cs | 14 ++-- .../Controllers/CalendarController.cs | 18 +++-- .../Controllers/CommonController.cs | 10 ++- .../Controllers/DropdownOptionsController.cs | 8 ++- .../Controllers/FollowUpController.cs | 12 ++-- .../Controllers/LocationController.cs | 12 ++-- .../Controllers/QuotesController.cs | 12 ++-- .../Controllers/SettingsController.cs | 18 +++-- .../Controllers/TaskListTemplateController.cs | 10 ++- .../Controllers/UpliftController.cs | 18 +++-- .../Controllers/UserController.cs | 14 ++-- .../Controllers/VendorController.cs | 20 +++--- .../Controllers/VendorPortalController.cs | 22 +++--- .../Controllers/WorkOrderController.cs | 33 +++++---- .../Helper/SanitizedErrors.cs | 27 +++++++ 26 files changed, 386 insertions(+), 99 deletions(-) create mode 100644 Api.SeaHavenIndustries.Tests/SanitizedErrorsTests.cs create mode 100644 Api.SeaHavenIndustries/Helper/SanitizedErrors.cs diff --git a/Api.SeaHavenIndustries.Tests/AuthenticationControllerTests.cs b/Api.SeaHavenIndustries.Tests/AuthenticationControllerTests.cs index d68c0ff..99d5ab8 100644 --- a/Api.SeaHavenIndustries.Tests/AuthenticationControllerTests.cs +++ b/Api.SeaHavenIndustries.Tests/AuthenticationControllerTests.cs @@ -4,6 +4,7 @@ using Data.SeaHavenIndustries; using FluentAssertions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Moq; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -20,7 +21,7 @@ public class AuthenticationControllerTests private static AuthenticationController NewController(Mock service, string? userId = null) { - var controller = new AuthenticationController(service.Object); + var controller = new AuthenticationController(service.Object, Mock.Of>()); if (userId != null) { controller.ControllerContext = new ControllerContext @@ -82,6 +83,35 @@ public class AuthenticationControllerTests result.Should().BeOfType(); } + [Fact] + public async Task Login_WhenServiceThrows_ReturnsSanitized500AndLogsInternally() + { + var service = new Mock(); + service.Setup(s => s.LoginAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ThrowsAsync(new InvalidOperationException("SECRET-internal-stack-detail")); + + var logger = new Mock>(); + logger.Setup(x => x.IsEnabled(It.IsAny())).Returns(true); + var controller = new AuthenticationController(service.Object, logger.Object); + + var result = await controller.Login(new LoginModel { Username = "x", Password = "y" }, CancellationToken.None); + + var status = result.Should().BeOfType().Subject; + status.StatusCode.Should().Be(500); + var message = (string)status.Value!; + message.Should().NotContain("SECRET"); + message.Should().NotContain("internal-stack-detail"); + message.Should().Contain("reference"); + logger.Verify( + x => x.Log( + LogLevel.Error, + It.IsAny(), + It.IsAny(), + It.Is(ex => ex.Message.Contains("SECRET")), + It.IsAny>()), + Times.Once); + } + [Fact] public async Task ChangePassword_Success_ReturnsSuccessMessageWithTrailingSpace() { diff --git a/Api.SeaHavenIndustries.Tests/CalendarControllerTests.cs b/Api.SeaHavenIndustries.Tests/CalendarControllerTests.cs index 306bece..8c2299f 100644 --- a/Api.SeaHavenIndustries.Tests/CalendarControllerTests.cs +++ b/Api.SeaHavenIndustries.Tests/CalendarControllerTests.cs @@ -4,6 +4,7 @@ using Api.SeaHavenIndustries.DTOs; using Data.SeaHavenIndustries; using FluentAssertions; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Moq; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -26,7 +27,7 @@ public class CalendarControllerTests }; private static CalendarController NewController(Mock service) => - new(service.Object); + new(service.Object, Mock.Of>()); private static object Prop(object source, string name) => source.GetType().GetProperty(name)!.GetValue(source)!; @@ -45,17 +46,32 @@ public class CalendarControllerTests } [Fact] - public async Task AddEvent_WhenServiceThrows_ReturnsBadRequestWithMessage() + public async Task AddEvent_WhenServiceThrows_ReturnsSanitizedMessageAndLogsInternally() { var service = new Mock(); service.Setup(s => s.CreateEventAsync(It.IsAny(), It.IsAny())) - .ThrowsAsync(new InvalidOperationException("boom")); + .ThrowsAsync(new InvalidOperationException("SECRET-internal-db-connection-leak")); - var result = await NewController(service).AddEvent(ValidDto(), CancellationToken.None); + var logger = new Mock>(); + logger.Setup(x => x.IsEnabled(It.IsAny())).Returns(true); + var controller = new CalendarController(service.Object, logger.Object); + + var result = await controller.AddEvent(ValidDto(), CancellationToken.None); var bad = result.Should().BeOfType().Subject; var response = bad.Value.Should().BeOfType().Subject; - response.Message.Should().Be("boom"); + response.Status.Should().Be("Error"); + response.Message.Should().NotContain("SECRET"); + response.Message.Should().NotContain("internal-db-connection-leak"); + response.Message.Should().Contain("reference"); + logger.Verify( + x => x.Log( + LogLevel.Error, + It.IsAny(), + It.IsAny(), + It.Is(ex => ex.Message.Contains("SECRET")), + It.IsAny>()), + Times.Once); } [Fact] diff --git a/Api.SeaHavenIndustries.Tests/FollowUpControllerTests.cs b/Api.SeaHavenIndustries.Tests/FollowUpControllerTests.cs index 85124ee..c91d2e4 100644 --- a/Api.SeaHavenIndustries.Tests/FollowUpControllerTests.cs +++ b/Api.SeaHavenIndustries.Tests/FollowUpControllerTests.cs @@ -5,6 +5,7 @@ using Data.SeaHavenIndustries; using FluentAssertions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Moq; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -16,7 +17,7 @@ public class FollowUpControllerTests { private static FollowUpController NewController(Mock service) { - var controller = new FollowUpController(service.Object) + var controller = new FollowUpController(service.Object, Mock.Of>()) { ControllerContext = new ControllerContext { @@ -33,7 +34,7 @@ public class FollowUpControllerTests } private static FollowUpController NewControllerNoUser(Mock service) => - new(service.Object) + new(service.Object, Mock.Of>()) { ControllerContext = new ControllerContext { diff --git a/Api.SeaHavenIndustries.Tests/LocationControllerTests.cs b/Api.SeaHavenIndustries.Tests/LocationControllerTests.cs index 6a326c5..d6dc573 100644 --- a/Api.SeaHavenIndustries.Tests/LocationControllerTests.cs +++ b/Api.SeaHavenIndustries.Tests/LocationControllerTests.cs @@ -4,6 +4,7 @@ using Data.SeaHavenIndustries; using FluentAssertions; using FluentValidation; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Moq; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -14,7 +15,7 @@ namespace Api.SeaHavenIndustries.Tests; public class LocationControllerTests { private static LocationController NewController(Mock service) => - new(service.Object); + new(service.Object, Mock.Of>()); private static object Prop(object source, string name) => source.GetType().GetProperty(name)!.GetValue(source)!; diff --git a/Api.SeaHavenIndustries.Tests/SanitizedErrorsTests.cs b/Api.SeaHavenIndustries.Tests/SanitizedErrorsTests.cs new file mode 100644 index 0000000..c8a5a81 --- /dev/null +++ b/Api.SeaHavenIndustries.Tests/SanitizedErrorsTests.cs @@ -0,0 +1,71 @@ +using Api.SeaHavenIndustries.Helper; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; + +namespace Api.SeaHavenIndustries.Tests; + +public class SanitizedErrorsTests +{ + [Fact] + public void Sanitize_ReturnsStableMessageAndNeverExposesExceptionText() + { + var logger = new Mock(); + logger.Setup(x => x.IsEnabled(It.IsAny())).Returns(true); + + var message = logger.Object.Sanitize(new InvalidOperationException("SUPER-SECRET-internal-detail")); + + message.Should().NotContain("SUPER-SECRET"); + message.Should().NotContain("internal-detail"); + message.Should().StartWith("An unexpected error occurred"); + message.Should().Contain("reference"); + } + + [Fact] + public void Sanitize_UsesProvidedPublicMessageInsteadOfExceptionText() + { + var logger = new Mock(); + logger.Setup(x => x.IsEnabled(It.IsAny())).Returns(true); + + var message = logger.Object.Sanitize( + new InvalidOperationException("SUPER-SECRET-internal-detail"), + "Vendor not found"); + + message.Should().Contain("Vendor not found"); + message.Should().NotContain("SUPER-SECRET"); + message.Should().Contain("reference"); + } + + [Fact] + public void Sanitize_LogsOriginalExceptionAtErrorLevel() + { + var logger = new Mock(); + logger.Setup(x => x.IsEnabled(It.IsAny())).Returns(true); + var exception = new InvalidOperationException("SUPER-SECRET-internal-detail"); + + logger.Object.Sanitize(exception); + + logger.Verify( + x => x.Log( + LogLevel.Error, + It.IsAny(), + It.IsAny(), + It.Is(ex => ex == exception), + It.IsAny>()), + Times.Once); + } + + [Fact] + public void Sanitize_ProducesUniqueCorrelationReferencesAcrossCalls() + { + var logger = new Mock(); + logger.Setup(x => x.IsEnabled(It.IsAny())).Returns(true); + var exception = new InvalidOperationException("detail"); + + var first = logger.Object.Sanitize(exception); + var second = logger.Object.Sanitize(exception); + + first.Should().NotBe(second); + } +} diff --git a/Api.SeaHavenIndustries.Tests/SettingsControllerTests.cs b/Api.SeaHavenIndustries.Tests/SettingsControllerTests.cs index aad00cd..b9f3502 100644 --- a/Api.SeaHavenIndustries.Tests/SettingsControllerTests.cs +++ b/Api.SeaHavenIndustries.Tests/SettingsControllerTests.cs @@ -4,6 +4,7 @@ using Api.SeaHavenIndustries.Helper.ViewModel; using Data.SeaHavenIndustries; using FluentAssertions; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Moq; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -14,7 +15,7 @@ namespace Api.SeaHavenIndustries.Tests; public class SettingsControllerTests { private static SettingsController NewController(Mock service) => - new(service.Object); + new(service.Object, Mock.Of>()); [Fact] public async Task GetCategoryList_ReturnsPaginationEnvelopeWithServiceItems() diff --git a/Api.SeaHavenIndustries.Tests/UpliftControllerTests.cs b/Api.SeaHavenIndustries.Tests/UpliftControllerTests.cs index 7c2f89f..d5a031e 100644 --- a/Api.SeaHavenIndustries.Tests/UpliftControllerTests.cs +++ b/Api.SeaHavenIndustries.Tests/UpliftControllerTests.cs @@ -3,6 +3,7 @@ using Data.SeaHavenIndustries; using FluentAssertions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Moq; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -15,7 +16,7 @@ public class UpliftControllerTests { private static UpliftController NewController(Mock service, params string[] roles) { - var controller = new UpliftController(service.Object); + var controller = new UpliftController(service.Object, Mock.Of>()); var claims = new List { new(ClaimTypes.NameIdentifier, "42") }; claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r))); controller.ControllerContext = new ControllerContext @@ -66,6 +67,41 @@ public class UpliftControllerTests nf.Value.Should().BeOfType(); } + [Fact] + public async Task Approve_Forbidden_ReturnsSanitized403AndLogsInternally() + { + var service = new Mock(); + service.Setup(x => x.ApproveAsync(It.IsAny(), 5, null, It.IsAny())) + .ThrowsAsync(new UpliftForbiddenException("SECRET-tier-restriction-detail")); + + var logger = new Mock>(); + logger.Setup(x => x.IsEnabled(It.IsAny())).Returns(true); + var controller = new UpliftController(service.Object, logger.Object); + controller.ControllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext + { + User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "42") }, "Test")) + } + }; + + var result = await controller.Approve(5, null); + + var status = result.Should().BeOfType().Subject; + status.StatusCode.Should().Be(403); + var response = status.Value.Should().BeOfType().Subject; + response.Message.Should().NotContain("SECRET"); + response.Message.Should().Contain("reference"); + logger.Verify( + x => x.Log( + LogLevel.Error, + It.IsAny(), + It.IsAny(), + It.Is(ex => ex.Message.Contains("SECRET")), + It.IsAny>()), + Times.Once); + } + [Fact] public async Task Approve_ForbiddenTier_Returns403() { diff --git a/Api.SeaHavenIndustries.Tests/UserControllerTests.cs b/Api.SeaHavenIndustries.Tests/UserControllerTests.cs index 1754479..620e461 100644 --- a/Api.SeaHavenIndustries.Tests/UserControllerTests.cs +++ b/Api.SeaHavenIndustries.Tests/UserControllerTests.cs @@ -4,6 +4,7 @@ using Data.SeaHavenIndustries; using FluentAssertions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Moq; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -20,7 +21,7 @@ public class UserControllerTests private static UserController NewController(Mock service, string? userId = null) { - var controller = new UserController(service.Object); + var controller = new UserController(service.Object, Mock.Of>()); if (userId != null) { controller.ControllerContext = new ControllerContext diff --git a/Api.SeaHavenIndustries.Tests/VendorControllerTests.cs b/Api.SeaHavenIndustries.Tests/VendorControllerTests.cs index 2f73a12..e7652b8 100644 --- a/Api.SeaHavenIndustries.Tests/VendorControllerTests.cs +++ b/Api.SeaHavenIndustries.Tests/VendorControllerTests.cs @@ -4,6 +4,7 @@ using Data.SeaHavenIndustries; using FluentAssertions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Moq; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -16,7 +17,7 @@ public class VendorControllerTests { private static VendorController NewController(Mock service) { - var controller = new VendorController(service.Object); + var controller = new VendorController(service.Object, Mock.Of>()); controller.ControllerContext = new ControllerContext { @@ -218,6 +219,44 @@ public class VendorControllerTests result.Should().BeOfType(); } + [Fact] + public async Task Delete_WhenDeactivationBlocked_ReturnsSanitizedConflictWithOpenWorkOrdersAndLogs() + { + var openWorkOrders = new List { new() { WorkOrderId = 7 } }; + var service = new Mock(); + service.Setup(x => x.DeleteVendorAsync(It.IsAny(), It.IsAny())) + .ThrowsAsync(new VendorDeactivationBlockedException("SECRET-internal-reason", openWorkOrders)); + + var logger = new Mock>(); + logger.Setup(x => x.IsEnabled(It.IsAny())).Returns(true); + var controller = new VendorController(service.Object, logger.Object); + controller.ControllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext + { + User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "42") }, "Test")) + } + }; + + var result = await controller.Delete(5); + + var conflict = result.Should().BeOfType().Subject; + var value = conflict.Value!; + var message = (string)value.GetType().GetProperty("Message")!.GetValue(value)!; + message.Should().NotContain("SECRET"); + message.Should().Contain("reference"); + var returnedOpenWorkOrders = (List)value.GetType().GetProperty("OpenWorkOrders")!.GetValue(value)!; + returnedOpenWorkOrders.Should().HaveCount(1); + logger.Verify( + x => x.Log( + LogLevel.Error, + It.IsAny(), + It.IsAny(), + It.Is(ex => ex.Message.Contains("SECRET")), + It.IsAny>()), + Times.Once); + } + [Fact] public async Task Delete_MapsDeactivationBlocked_ToConflict() { @@ -265,7 +304,7 @@ public class VendorControllerTests public async Task Update_UnauthenticatedUser_ReturnsUnauthorized() { var service = new Mock(); - var controller = new VendorController(service.Object); + var controller = new VendorController(service.Object, Mock.Of>()); controller.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext { User = new ClaimsPrincipal() } diff --git a/Api.SeaHavenIndustries.Tests/VendorPortalControllerTests.cs b/Api.SeaHavenIndustries.Tests/VendorPortalControllerTests.cs index d2900c1..3060962 100644 --- a/Api.SeaHavenIndustries.Tests/VendorPortalControllerTests.cs +++ b/Api.SeaHavenIndustries.Tests/VendorPortalControllerTests.cs @@ -3,6 +3,7 @@ using Data.SeaHavenIndustries; using FluentAssertions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Moq; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -14,7 +15,7 @@ public class VendorPortalControllerTests { private static VendorPortalController NewController(Mock service, string? token = "valid-token") { - var controller = new VendorPortalController(service.Object); + var controller = new VendorPortalController(service.Object, Mock.Of>()); var context = new DefaultHttpContext(); if (token != null) context.Request.Headers["X-Vendor-Token"] = token; @@ -133,7 +134,8 @@ public class VendorPortalControllerTests var bad = result.Should().BeOfType().Subject; var resp = bad.Value.Should().BeOfType().Subject; - resp.Message.Should().Be("Cannot accept"); + resp.Message.Should().NotContain("Cannot accept"); + resp.Message.Should().Contain("reference"); } [Fact] @@ -169,7 +171,8 @@ public class VendorPortalControllerTests var bad = result.Should().BeOfType().Subject; var resp = bad.Value.Should().BeOfType().Subject; - resp.Message.Should().Contain("Transition"); + resp.Message.Should().NotContain("Transition"); + resp.Message.Should().Contain("reference"); } [Fact] diff --git a/Api.SeaHavenIndustries/Api.SeaHavenIndustries.csproj b/Api.SeaHavenIndustries/Api.SeaHavenIndustries.csproj index b1c8fc5..9de9eaf 100644 --- a/Api.SeaHavenIndustries/Api.SeaHavenIndustries.csproj +++ b/Api.SeaHavenIndustries/Api.SeaHavenIndustries.csproj @@ -24,4 +24,8 @@ + + + + diff --git a/Api.SeaHavenIndustries/Controllers/AuthenticationController.cs b/Api.SeaHavenIndustries/Controllers/AuthenticationController.cs index 67a5d33..12fd8fa 100644 --- a/Api.SeaHavenIndustries/Controllers/AuthenticationController.cs +++ b/Api.SeaHavenIndustries/Controllers/AuthenticationController.cs @@ -1,7 +1,9 @@ using Api.SeaHavenIndustries.DTOs; +using Api.SeaHavenIndustries.Helper; using Data.SeaHavenIndustries; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; using System.Security.Claims; @@ -13,10 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers public class AuthenticationController : Controller { private readonly IAuthenticationService _authenticationService; + private readonly ILogger _logger; - public AuthenticationController(IAuthenticationService authenticationService) + public AuthenticationController(IAuthenticationService authenticationService, ILogger logger) { _authenticationService = authenticationService; + _logger = logger; } [AllowAnonymous] @@ -46,7 +50,7 @@ namespace Api.SeaHavenIndustries.Controllers catch (Exception ex) { - return StatusCode(500, ex.Message); + return StatusCode(500, _logger.Sanitize(ex)); } } @@ -94,7 +98,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -135,7 +139,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = "Error: " + ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -159,7 +163,7 @@ namespace Api.SeaHavenIndustries.Controllers catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = "Error: " + ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } diff --git a/Api.SeaHavenIndustries/Controllers/CalendarController.cs b/Api.SeaHavenIndustries/Controllers/CalendarController.cs index caa18be..b54b0b7 100644 --- a/Api.SeaHavenIndustries/Controllers/CalendarController.cs +++ b/Api.SeaHavenIndustries/Controllers/CalendarController.cs @@ -1,7 +1,9 @@ using Api.SeaHavenIndustries.DTOs; +using Api.SeaHavenIndustries.Helper; using Data.SeaHavenIndustries; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -13,10 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers public class CalendarController : Controller { private readonly ICalendarService _calendarService; + private readonly ILogger _logger; - public CalendarController(ICalendarService calendarService) + public CalendarController(ICalendarService calendarService, ILogger logger) { _calendarService = calendarService; + _logger = logger; } // =================================================================================== @@ -34,7 +38,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -62,7 +66,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -86,7 +90,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -109,7 +113,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -135,7 +139,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -154,7 +158,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } diff --git a/Api.SeaHavenIndustries/Controllers/CommonController.cs b/Api.SeaHavenIndustries/Controllers/CommonController.cs index 875834e..f1624b4 100644 --- a/Api.SeaHavenIndustries/Controllers/CommonController.cs +++ b/Api.SeaHavenIndustries/Controllers/CommonController.cs @@ -1,7 +1,9 @@ using Api.SeaHavenIndustries.DTOs; +using Api.SeaHavenIndustries.Helper; using Api.SeaHavenIndustries.Helper.ViewModel; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using SeaHaven.Services.Interfaces; namespace Api.SeaHavenIndustries.Controllers @@ -13,10 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers public class CommonController : Controller { private readonly ICommonService _commonService; + private readonly ILogger _logger; - public CommonController(ICommonService commonService) + public CommonController(ICommonService commonService, ILogger logger) { _commonService = commonService; + _logger = logger; } [HttpGet] @@ -49,7 +53,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new { Status = "Error", Message = ex.Message }); + return BadRequest(new { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -65,7 +69,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new { Status = "Error", Message = ex.Message }); + return BadRequest(new { Status = "Error", Message = _logger.Sanitize(ex) }); } } diff --git a/Api.SeaHavenIndustries/Controllers/DropdownOptionsController.cs b/Api.SeaHavenIndustries/Controllers/DropdownOptionsController.cs index 1085c5d..18c0c04 100644 --- a/Api.SeaHavenIndustries/Controllers/DropdownOptionsController.cs +++ b/Api.SeaHavenIndustries/Controllers/DropdownOptionsController.cs @@ -1,6 +1,8 @@ using Api.SeaHavenIndustries.DTOs; +using Api.SeaHavenIndustries.Helper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -12,10 +14,12 @@ namespace Api.SeaHavenIndustries.Controllers public class DropdownOptionsController : Controller { private readonly IDropdownOptionsService _dropdownOptionsService; + private readonly ILogger _logger; - public DropdownOptionsController(IDropdownOptionsService dropdownOptionsService) + public DropdownOptionsController(IDropdownOptionsService dropdownOptionsService, ILogger logger) { _dropdownOptionsService = dropdownOptionsService; + _logger = logger; } [HttpGet] @@ -42,7 +46,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (InvalidOperationException ex) { - return BadRequest(new { message = ex.Message }); + return BadRequest(new { message = _logger.Sanitize(ex, "The dropdown option could not be saved") }); } } diff --git a/Api.SeaHavenIndustries/Controllers/FollowUpController.cs b/Api.SeaHavenIndustries/Controllers/FollowUpController.cs index 35099eb..73e2860 100644 --- a/Api.SeaHavenIndustries/Controllers/FollowUpController.cs +++ b/Api.SeaHavenIndustries/Controllers/FollowUpController.cs @@ -1,9 +1,11 @@ using System.Security.Claims; using Api.SeaHavenIndustries.DTOs; +using Api.SeaHavenIndustries.Helper; using Data.SeaHavenIndustries; using FluentValidation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -14,10 +16,12 @@ using SeaHaven.Services.Interfaces; public class FollowUpController : ControllerBase { private readonly IFollowUpService _followUpService; + private readonly ILogger _logger; - public FollowUpController(IFollowUpService followUpService) + public FollowUpController(IFollowUpService followUpService, ILogger logger) { _followUpService = followUpService; + _logger = logger; } [HttpGet("GetFollowUpList")] @@ -111,7 +115,7 @@ public class FollowUpController : ControllerBase } catch (Exception ex) { - return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); + return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -134,7 +138,7 @@ public class FollowUpController : ControllerBase } catch (Exception ex) { - return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); + return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -173,7 +177,7 @@ public class FollowUpController : ControllerBase } catch (Exception ex) { - return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); + return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } diff --git a/Api.SeaHavenIndustries/Controllers/LocationController.cs b/Api.SeaHavenIndustries/Controllers/LocationController.cs index b974264..47962c0 100644 --- a/Api.SeaHavenIndustries/Controllers/LocationController.cs +++ b/Api.SeaHavenIndustries/Controllers/LocationController.cs @@ -1,8 +1,10 @@ using Api.SeaHavenIndustries.DTOs; +using Api.SeaHavenIndustries.Helper; using Data.SeaHavenIndustries; using FluentValidation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -15,10 +17,12 @@ namespace Api.SeaHavenIndustries.Controllers public class LocationController : Controller { private readonly ILocationService _locationService; + private readonly ILogger _logger; - public LocationController(ILocationService locationService) + public LocationController(ILocationService locationService, ILogger logger) { _locationService = locationService; + _logger = logger; } [HttpGet("GetLocationList")] @@ -98,7 +102,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); + return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -121,7 +125,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); + return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -142,7 +146,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); + return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } diff --git a/Api.SeaHavenIndustries/Controllers/QuotesController.cs b/Api.SeaHavenIndustries/Controllers/QuotesController.cs index 6967159..97e111b 100644 --- a/Api.SeaHavenIndustries/Controllers/QuotesController.cs +++ b/Api.SeaHavenIndustries/Controllers/QuotesController.cs @@ -1,7 +1,9 @@ using Api.SeaHavenIndustries.DTOs; +using Api.SeaHavenIndustries.Helper; using Data.SeaHavenIndustries; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -13,10 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers public class QuotesController : Controller { private readonly IQuotesService _quotesService; + private readonly ILogger _logger; - public QuotesController(IQuotesService quotesService) + public QuotesController(IQuotesService quotesService, ILogger logger) { _quotesService = quotesService; + _logger = logger; } [HttpPost] @@ -31,7 +35,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -49,7 +53,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -68,7 +72,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } diff --git a/Api.SeaHavenIndustries/Controllers/SettingsController.cs b/Api.SeaHavenIndustries/Controllers/SettingsController.cs index 38c31b4..c1be8fc 100644 --- a/Api.SeaHavenIndustries/Controllers/SettingsController.cs +++ b/Api.SeaHavenIndustries/Controllers/SettingsController.cs @@ -1,8 +1,10 @@ using Api.SeaHavenIndustries.DTOs; +using Api.SeaHavenIndustries.Helper; using Api.SeaHavenIndustries.Helper.ViewModel; using Data.SeaHavenIndustries; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using SeaHaven.Services.Interfaces; namespace Api.SeaHavenIndustries.Controllers @@ -13,10 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers public class SettingsController : Controller { private readonly ISettingsService _settingsService; + private readonly ILogger _logger; - public SettingsController(ISettingsService settingsService) + public SettingsController(ISettingsService settingsService, ILogger logger) { _settingsService = settingsService; + _logger = logger; } [HttpPost] @@ -30,7 +34,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -47,7 +51,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -64,7 +68,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -109,7 +113,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -145,7 +149,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new { Status = "Error", Message = ex.Message }); + return BadRequest(new { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -161,7 +165,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } } diff --git a/Api.SeaHavenIndustries/Controllers/TaskListTemplateController.cs b/Api.SeaHavenIndustries/Controllers/TaskListTemplateController.cs index b4b62c4..b3bd441 100644 --- a/Api.SeaHavenIndustries/Controllers/TaskListTemplateController.cs +++ b/Api.SeaHavenIndustries/Controllers/TaskListTemplateController.cs @@ -1,7 +1,9 @@ using Api.SeaHavenIndustries.DTOs; +using Api.SeaHavenIndustries.Helper; using Data.SeaHavenIndustries; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -14,10 +16,12 @@ namespace Api.SeaHavenIndustries.Controllers public class TaskListTemplateController : Controller { private readonly ITaskListTemplateService _taskListTemplateService; + private readonly ILogger _logger; - public TaskListTemplateController(ITaskListTemplateService taskListTemplateService) + public TaskListTemplateController(ITaskListTemplateService taskListTemplateService, ILogger logger) { _taskListTemplateService = taskListTemplateService; + _logger = logger; } [HttpGet] @@ -49,7 +53,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -67,7 +71,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } diff --git a/Api.SeaHavenIndustries/Controllers/UpliftController.cs b/Api.SeaHavenIndustries/Controllers/UpliftController.cs index 73d7d5d..7cccbb7 100644 --- a/Api.SeaHavenIndustries/Controllers/UpliftController.cs +++ b/Api.SeaHavenIndustries/Controllers/UpliftController.cs @@ -1,6 +1,8 @@ +using Api.SeaHavenIndustries.Helper; using Data.SeaHavenIndustries; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -13,10 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers public class UpliftController : Controller { private readonly IUpliftService _upliftService; + private readonly ILogger _logger; - public UpliftController(IUpliftService upliftService) + public UpliftController(IUpliftService upliftService, ILogger logger) { _upliftService = upliftService; + _logger = logger; } [HttpGet] @@ -43,15 +47,15 @@ namespace Api.SeaHavenIndustries.Controllers } catch (KeyNotFoundException ex) { - return NotFound(new Response { Status = "Error", Message = ex.Message }); + return NotFound(new Response { Status = "Error", Message = _logger.Sanitize(ex, "Uplift request not found") }); } catch (UpliftForbiddenException ex) { - return StatusCode(403, new Response { Status = "Error", Message = ex.Message }); + return StatusCode(403, new Response { Status = "Error", Message = _logger.Sanitize(ex, "You are not authorized to perform this action on this uplift request") }); } catch (InvalidOperationException ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "This uplift request cannot be modified in its current state") }); } } @@ -65,15 +69,15 @@ namespace Api.SeaHavenIndustries.Controllers } catch (KeyNotFoundException ex) { - return NotFound(new Response { Status = "Error", Message = ex.Message }); + return NotFound(new Response { Status = "Error", Message = _logger.Sanitize(ex, "Uplift request not found") }); } catch (UpliftForbiddenException ex) { - return StatusCode(403, new Response { Status = "Error", Message = ex.Message }); + return StatusCode(403, new Response { Status = "Error", Message = _logger.Sanitize(ex, "You are not authorized to perform this action on this uplift request") }); } catch (InvalidOperationException ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "This uplift request cannot be modified in its current state") }); } } diff --git a/Api.SeaHavenIndustries/Controllers/UserController.cs b/Api.SeaHavenIndustries/Controllers/UserController.cs index 39aa1bf..1540c5c 100644 --- a/Api.SeaHavenIndustries/Controllers/UserController.cs +++ b/Api.SeaHavenIndustries/Controllers/UserController.cs @@ -1,7 +1,9 @@ using Api.SeaHavenIndustries.DTOs; +using Api.SeaHavenIndustries.Helper; using Data.SeaHavenIndustries; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; using System.Security.Claims; @@ -14,10 +16,12 @@ namespace Api.SeaHavenIndustries.Controllers public class UserController : Controller { private readonly IUserService _userService; + private readonly ILogger _logger; - public UserController(IUserService userService) + public UserController(IUserService userService, ILogger logger) { _userService = userService; + _logger = logger; } [Route("")] [HttpGet] @@ -49,7 +53,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -73,7 +77,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -93,7 +97,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -109,7 +113,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } diff --git a/Api.SeaHavenIndustries/Controllers/VendorController.cs b/Api.SeaHavenIndustries/Controllers/VendorController.cs index 9eafd77..06251bc 100644 --- a/Api.SeaHavenIndustries/Controllers/VendorController.cs +++ b/Api.SeaHavenIndustries/Controllers/VendorController.cs @@ -1,8 +1,10 @@ using Api.SeaHavenIndustries.DTOs; +using Api.SeaHavenIndustries.Helper; using Data.SeaHavenIndustries; using FluentValidation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using SeaHaven.Services.Interfaces; using SeaHaven.Services.DTOs; using System.Security.Claims; @@ -16,10 +18,12 @@ namespace Api.SeaHavenIndustries.Controllers public class VendorController : Controller { private readonly IVendorService _vendorService; + private readonly ILogger _logger; - public VendorController(IVendorService vendorService) + public VendorController(IVendorService vendorService, ILogger logger) { _vendorService = vendorService; + _logger = logger; } [HttpGet("GetVendorList")] @@ -148,7 +152,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); + return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -199,7 +203,7 @@ namespace Api.SeaHavenIndustries.Controllers return Conflict(new { Status = "Conflict", - Message = dbex.Message, + Message = _logger.Sanitize(dbex, "Vendor cannot be deactivated because it has open work orders"), OpenWorkOrders = dbex.OpenWorkOrders }); } @@ -209,7 +213,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); + return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -236,7 +240,7 @@ namespace Api.SeaHavenIndustries.Controllers return Conflict(new { Status = "Conflict", - Message = dbex.Message, + Message = _logger.Sanitize(dbex, "Vendor cannot be deactivated because it has open work orders"), OpenWorkOrders = dbex.OpenWorkOrders }); } @@ -246,7 +250,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); + return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -264,7 +268,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); + return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -304,7 +308,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); + return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } diff --git a/Api.SeaHavenIndustries/Controllers/VendorPortalController.cs b/Api.SeaHavenIndustries/Controllers/VendorPortalController.cs index dd2d82d..2619ba8 100644 --- a/Api.SeaHavenIndustries/Controllers/VendorPortalController.cs +++ b/Api.SeaHavenIndustries/Controllers/VendorPortalController.cs @@ -1,5 +1,7 @@ +using Api.SeaHavenIndustries.Helper; using Data.SeaHavenIndustries; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; @@ -13,10 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers private const string TokenHeader = "X-Vendor-Token"; private readonly IVendorPortalService _portalService; + private readonly ILogger _logger; - public VendorPortalController(IVendorPortalService portalService) + public VendorPortalController(IVendorPortalService portalService, ILogger logger) { _portalService = portalService; + _logger = logger; } [HttpGet("session")] @@ -71,7 +75,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (InvalidOperationException ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") }); } } @@ -92,7 +96,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (InvalidOperationException ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") }); } } @@ -113,7 +117,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (InvalidOperationException ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") }); } } @@ -134,7 +138,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (InvalidOperationException ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") }); } } @@ -155,7 +159,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (InvalidOperationException ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") }); } } @@ -176,7 +180,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (InvalidOperationException ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") }); } } @@ -197,7 +201,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (InvalidOperationException ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") }); } } @@ -218,7 +222,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (InvalidOperationException ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") }); } } diff --git a/Api.SeaHavenIndustries/Controllers/WorkOrderController.cs b/Api.SeaHavenIndustries/Controllers/WorkOrderController.cs index e8b5c02..0a0c0a4 100644 --- a/Api.SeaHavenIndustries/Controllers/WorkOrderController.cs +++ b/Api.SeaHavenIndustries/Controllers/WorkOrderController.cs @@ -1,8 +1,10 @@ using Api.SeaHavenIndustries.DTOs; +using Api.SeaHavenIndustries.Helper; using Data.SeaHavenIndustries; using FluentValidation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; using SeaHaven.DataServices.Models; @@ -18,13 +20,16 @@ namespace Api.SeaHavenIndustries.Controllers { private readonly IWorkOrderService _workOrderService; private readonly IWorkOrderDispatchService _dispatchService; + private readonly ILogger _logger; public WorkOrderController( IWorkOrderService workOrderService, - IWorkOrderDispatchService dispatchService) + IWorkOrderDispatchService dispatchService, + ILogger logger) { _workOrderService = workOrderService; _dispatchService = dispatchService; + _logger = logger; } [HttpPost] @@ -60,7 +65,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -107,7 +112,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -149,7 +154,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -238,7 +243,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -257,7 +262,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -281,7 +286,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -304,7 +309,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -365,7 +370,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -410,7 +415,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -436,7 +441,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -458,7 +463,7 @@ namespace Api.SeaHavenIndustries.Controllers } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } @@ -484,11 +489,11 @@ namespace Api.SeaHavenIndustries.Controllers } catch (InvalidOperationException ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The dispatch signoff could not be added") }); } catch (Exception ex) { - return BadRequest(new Response { Status = "Error", Message = ex.Message }); + return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) }); } } diff --git a/Api.SeaHavenIndustries/Helper/SanitizedErrors.cs b/Api.SeaHavenIndustries/Helper/SanitizedErrors.cs new file mode 100644 index 0000000..1393770 --- /dev/null +++ b/Api.SeaHavenIndustries/Helper/SanitizedErrors.cs @@ -0,0 +1,27 @@ +using System.Globalization; +using System.Runtime.CompilerServices; +using Microsoft.Extensions.Logging; + +namespace Api.SeaHavenIndustries.Helper; + +internal static class SanitizedErrors +{ + private const string DefaultPublicMessage = "An unexpected error occurred"; + + public static string Sanitize( + this ILogger logger, + Exception exception, + string? publicMessage = null, + [CallerMemberName] string operation = "") + { + var correlationId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); + + logger.LogError( + exception, + "Unhandled exception in {Operation}. CorrelationId: {CorrelationId}", + operation, + correlationId); + + return $"{publicMessage ?? DefaultPublicMessage} (reference {correlationId})."; + } +}