Compare commits

..

3 Commits

Author SHA1 Message Date
Developer 02
7fefc68061 Refactor enum handling in MemoryCache and HistoryController
Updated `GetEnumAsDictionary<TEnum>` in `MemoryCacheExtensions.cs` to use a loop for populating a dictionary of enum values, removing LINQ for simplicity.

Modified `HistoryController.cs` to adjust method signatures for `GetReferenceTypes` and `GetEnvelopeStatus`, allowing optional parameters for better conditional responses. Added necessary using directives.
2025-05-08 12:00:48 +02:00
Developer 02
3035ec7e9c Add memory caching support in HistoryController
- Updated `EnvelopeGenerator.Extensions.csproj` to include
  `Microsoft.Extensions.Caching.Memory` package.
- Refactored `HistoryController` to use `IMemoryCache` for
  caching functionality.
- Replaced manual dictionary creation in `GetReferenceTypes`
  with a call to `GetEnumAsDictionary<ReferenceType>()`.
- Changed enum type in `GetEnvelopeStatus` to
  `ReferenceType` for consistency.
- Introduced `MemoryCacheExtensions` class with
  `GetEnumAsDictionary<TEnum>` method for efficient enum
  to dictionary conversion.
2025-05-08 11:30:46 +02:00
Developer 02
3a1fe45524 Add "OrDefault" methods for user claim retrieval
Introduce new extension methods in `ControllerExtensions` to safely extract user information from a `ClaimsPrincipal`. Updated methods for ID, username, surname, given name, and email to return `null` if not found, enhancing flexibility. Updated `EnvelopeReceiverController` to utilize these new methods for improved handling of absent user claims.
2025-05-08 10:56:46 +02:00
5 changed files with 76 additions and 35 deletions

View File

@@ -13,6 +13,7 @@
<ItemGroup>
<PackageReference Include="HtmlSanitizer" Version="8.0.865" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.19" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Otp.NET" Version="1.4.0" />

View File

@@ -0,0 +1,20 @@
using Microsoft.Extensions.Caching.Memory;
using System;
namespace EnvelopeGenerator.Extensions;
public static class MemoryCacheExtensions
{
public static IDictionary<string, int> GetEnumAsDictionary<TEnum>(this IMemoryCache memoryCache)
where TEnum : Enum
{
var dict = new Dictionary<string, int>();
foreach (TEnum role in Enum.GetValues(typeof(TEnum)))
{
dict[role.ToString()] = Convert.ToInt32(role);
}
return dict;
}
}

View File

@@ -3,26 +3,60 @@ using System.Security.Claims;
namespace EnvelopeGenerator.GeneratorAPI.Controllers
{
/// <summary>
/// Provides extension methods for extracting user information from a <see cref="ClaimsPrincipal"/>.
/// </summary>
public static class ControllerExtensions
{
/// <summary>
/// Attempts to retrieve the user's ID from the claims. Returns null if the ID is not found or invalid.
/// </summary>
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
/// <returns>The user's ID as an integer, or null if not found or invalid.</returns>
public static int? GetIdOrDefault(this ClaimsPrincipal user)
=> int.TryParse(user.FindFirstValue(ClaimTypes.NameIdentifier) ?? user.FindFirstValue("sub"), out int result)
? result : null;
/// <summary>
/// Retrieves the user's ID from the claims. Throws an exception if the ID is missing or invalid.
/// </summary>
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
/// <returns>The user's ID as an integer.</returns>
/// <exception cref="InvalidOperationException">Thrown if the user ID claim is missing or invalid.</exception>
public static int GetId(this ClaimsPrincipal user)
=> user.GetIdOrDefault()
?? throw new InvalidOperationException("User ID claim is missing or invalid. This may indicate a misconfigured or forged JWT token.");
public static string? GetUsername(this ClaimsPrincipal user)
/// <summary>
/// Retrieves the username from the claims, if available.
/// </summary>
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
/// <returns>The username as a string, or null if not found.</returns>
public static string? GetUsernameOrDefault(this ClaimsPrincipal user)
=> user.FindFirst(ClaimTypes.Name)?.Value;
public static string? GetName(this ClaimsPrincipal user)
/// <summary>
/// Retrieves the user's surname (last name) from the claims, if available.
/// </summary>
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
/// <returns>The surname as a string, or null if not found.</returns>
public static string? GetNameOrDefault(this ClaimsPrincipal user)
=> user.FindFirst(ClaimTypes.Surname)?.Value;
public static string? GetPrename(this ClaimsPrincipal user)
/// <summary>
/// Retrieves the user's given name (first name) from the claims, if available.
/// </summary>
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
/// <returns>The given name as a string, or null if not found.</returns>
public static string? GetPrenameOrDefault(this ClaimsPrincipal user)
=> user.FindFirst(ClaimTypes.GivenName)?.Value;
public static string? GetEmail(this ClaimsPrincipal user)
/// <summary>
/// Retrieves the user's email address from the claims, if available.
/// </summary>
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
/// <returns>The email address as a string, or null if not found.</returns>
public static string? GetEmailOrDefault(this ClaimsPrincipal user)
=> user.FindFirst(ClaimTypes.Email)?.Value;
}
}

View File

@@ -86,12 +86,12 @@ public class EnvelopeReceiverController : ControllerBase
{
try
{
var username = User.GetUsername();
var username = User.GetUsernameOrDefault();
if (username is null)
{
_logger.LogError(@"Envelope Receiver dto cannot be sent because username claim is null. Potential authentication and authorization error. The value of other claims are [id: {id}], [username: {username}], [name: {name}], [prename: {prename}], [email: {email}].",
User.GetId(), User.GetUsername(), User.GetName(), User.GetPrename(), User.GetEmail());
User.GetId(), User.GetUsernameOrDefault(), User.GetNameOrDefault(), User.GetPrenameOrDefault(), User.GetEmailOrDefault());
return StatusCode(StatusCodes.Status500InternalServerError);
}

View File

@@ -1,8 +1,11 @@
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
using EnvelopeGenerator.Application.Contracts.Services;
using EnvelopeGenerator.Application.Histories.Queries.Read;
using EnvelopeGenerator.Extensions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.IdentityModel.Tokens;
using static EnvelopeGenerator.Common.Constants;
@@ -20,15 +23,18 @@ public class HistoryController : ControllerBase
private readonly IEnvelopeHistoryService _service;
private readonly IMemoryCache _memoryCache;
/// <summary>
/// Konstruktor für den HistoryController.
/// </summary>
/// <param name="logger">Der Logger, der für das Protokollieren von Informationen verwendet wird.</param>
/// <param name="service">Der Dienst, der für die Verarbeitung der Umschlaghistorie verantwortlich ist.</param>
public HistoryController(ILogger<HistoryController> logger, IEnvelopeHistoryService service)
public HistoryController(ILogger<HistoryController> logger, IEnvelopeHistoryService service, IMemoryCache memoryCache)
{
_logger = logger;
_service = service;
_memoryCache = memoryCache;
}
/// <summary>
@@ -47,19 +53,9 @@ public class HistoryController : ControllerBase
/// <response code="200"></response>
[HttpGet("related")]
[Authorize]
public IActionResult GetReferenceTypes()
public IActionResult GetReferenceTypes(ReferenceType? referenceType = null)
{
// Enum zu Schlüssel-Wert-Paar
var referenceTypes = Enum.GetValues(typeof(ReferenceType))
.Cast<ReferenceType>()
.ToDictionary(rt =>
{
var key = rt.ToString();
var keyAsCamelCase = char.ToLower(key[0]) + key[1..];
return keyAsCamelCase;
}, rt => (int)rt);
return Ok(referenceTypes);
return referenceType is null ? Ok(_memoryCache.GetEnumAsDictionary<ReferenceType>()) : Ok(referenceType.ToString());
}
/// <summary>
@@ -91,7 +87,7 @@ public class HistoryController : ControllerBase
/// 3004: MessageDeletionSent
/// 3005: MessageCompletionSent
/// </summary>
/// <param name="related">
/// <param name="status">
/// Abfrageparameter, der angibt, auf welche Referenz sich der Status bezieht.
/// 0 - Sender: Historische Datensätze, die sich auf den Status des Absenders beziehen. Sie haben Statuscodes, die mit 1* beginnen.
/// 1 - Receiver: Historische Datensätze über den Status der Empfänger. Diese haben Statuscodes, die mit 2* beginnen.
@@ -102,19 +98,9 @@ public class HistoryController : ControllerBase
/// <response code="200"></response>
[HttpGet("status")]
[Authorize]
public IActionResult GetEnvelopeStatus([FromQuery] ReferenceType? related = null)
public IActionResult GetEnvelopeStatus([FromQuery] EnvelopeStatus? status = null)
{
// Enum zu Schlüssel-Wert-Paar
var referenceTypes = Enum.GetValues(typeof(EnvelopeStatus))
.Cast<EnvelopeStatus>()
.ToDictionary(rt =>
{
var key = rt.ToString();
var keyAsCamelCase = char.ToLower(key[0]) + key[1..];
return keyAsCamelCase;
}, rt => (int)rt);
return Ok(referenceTypes);
return status is null ? Ok(_memoryCache.GetEnumAsDictionary<EnvelopeStatus>()) : Ok(status.ToString());
}
/// <summary>