SF-74: Server — UserPreferencesController, claim extensions, DI registration, App.razor
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
<script src="_content/DevExpress.Blazor.Resources/js/preload-script.js"></script>
|
<script src="_content/DevExpress.Blazor.Resources/js/preload-script.js"></script>
|
||||||
<script src="js/typed.umd.js"></script>
|
<script src="js/typed.umd.js"></script>
|
||||||
<script src="js/receiver-signature.js?v=9"></script>
|
<script src="js/receiver-signature.js?v=9"></script>
|
||||||
|
<script src="js/signflow-theme.js"></script>
|
||||||
<script src="_framework/blazor.web.js"></script>
|
<script src="_framework/blazor.web.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -10,24 +10,37 @@ namespace EnvelopeGenerator.Server.Controllers;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Manages per-user UI preferences (theme, dark mode, grid layouts).
|
/// Manages per-user UI preferences (theme, dark mode, grid layouts).
|
||||||
/// Preferences are stored in the distributed cache (dbo.TBDD_CACHE).
|
/// Preferences are stored in the distributed cache (dbo.TBDD_CACHE).
|
||||||
|
/// Accessible by both Sender and Receiver roles.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
[Authorize(Policy = AuthPolicy.Sender)]
|
[Authorize(Policy = AuthPolicy.SenderOrReceiver)]
|
||||||
public class UserPreferencesController(IUserPreferencesService preferencesService) : ControllerBase
|
public class UserPreferencesController(IUserPreferencesService preferencesService) : ControllerBase
|
||||||
{
|
{
|
||||||
private static readonly HashSet<string> AllowedThemes =
|
private static readonly HashSet<string> AllowedThemes =
|
||||||
["blazing-berry", "fluent", "purple"];
|
["blazing-berry", "blazing-dark", "purple"];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the current user's UI preferences.
|
/// Returns the current user's UI preferences.
|
||||||
/// Always returns 200 with defaults if no preferences have been saved yet.
|
/// Always returns 200 with defaults if no preferences have been saved yet.
|
||||||
|
/// DisplayName is populated from JWT claims for senders only.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<UserPreferencesDto>> Get(CancellationToken ct)
|
public async Task<ActionResult<UserPreferencesDto>> Get(CancellationToken ct)
|
||||||
{
|
{
|
||||||
var username = User.GetUsername();
|
bool isReceiver = User.IsReceiver();
|
||||||
var prefs = await preferencesService.GetAsync(username, ct);
|
|
||||||
|
var userId = isReceiver ? User.ReceiverMail() : User.GetUsername();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(userId))
|
||||||
|
return Unauthorized();
|
||||||
|
|
||||||
|
var prefs = await preferencesService.GetOrCreateAsync(userId, ct);
|
||||||
|
|
||||||
|
// Populate DisplayName for sender only (receiver has no given/surname claims)
|
||||||
|
if (!isReceiver)
|
||||||
|
prefs.DisplayName = $"{User.GetPrename()} {User.GetName()}".Trim();
|
||||||
|
|
||||||
return Ok(prefs);
|
return Ok(prefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,8 +55,12 @@ public class UserPreferencesController(IUserPreferencesService preferencesServic
|
|||||||
if (!AllowedThemes.Contains(dto.ThemeName))
|
if (!AllowedThemes.Contains(dto.ThemeName))
|
||||||
return BadRequest($"Invalid theme '{dto.ThemeName}'. Allowed: {string.Join(", ", AllowedThemes)}.");
|
return BadRequest($"Invalid theme '{dto.ThemeName}'. Allowed: {string.Join(", ", AllowedThemes)}.");
|
||||||
|
|
||||||
var username = User.GetUsername();
|
var userId = User.IsReceiver() ? User.ReceiverMail() : User.GetUsername();
|
||||||
await preferencesService.SaveAsync(username, dto, ct);
|
|
||||||
|
if (string.IsNullOrWhiteSpace(userId))
|
||||||
|
return Unauthorized();
|
||||||
|
|
||||||
|
await preferencesService.SaveAsync(userId, dto, ct);
|
||||||
return Ok(dto);
|
return Ok(dto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using DigitalData.Auth.Claims;
|
using DigitalData.Auth.Claims;
|
||||||
|
using DigitalData.UserManager.Domain.Entities;
|
||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
using Microsoft.IdentityModel.JsonWebTokens;
|
using Microsoft.IdentityModel.JsonWebTokens;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
|
||||||
@@ -93,4 +95,14 @@ public static class ReceiverClaimExtensions
|
|||||||
else
|
else
|
||||||
throw new InvalidOperationException($"Claim '{EnvelopeClaimNames.ReceiverId}' is not a valid integer.");
|
throw new InvalidOperationException($"Claim '{EnvelopeClaimNames.ReceiverId}' is not a valid integer.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool IsReceiver(this ClaimsPrincipal user)
|
||||||
|
{
|
||||||
|
return Role.Receiver.Authorized.Any(user.IsInRole);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -271,7 +271,12 @@ try
|
|||||||
.AddAuthenticationSchemes(AuthScheme.Receiver)
|
.AddAuthenticationSchemes(AuthScheme.Receiver)
|
||||||
.RequireAuthenticatedUser()
|
.RequireAuthenticatedUser()
|
||||||
.RequireRole(Role.Receiver.Full, "receiver"))
|
.RequireRole(Role.Receiver.Full, "receiver"))
|
||||||
.AddPolicy(AuthPolicy.ReceiverTFA, policy => policy.RequireRole(Role.Receiver.TFA));
|
.AddPolicy(AuthPolicy.ReceiverTFA, policy => policy.RequireRole(Role.Receiver.TFA))
|
||||||
|
// Combined policy: accepts both Sender and Receiver tokens.
|
||||||
|
// Used by endpoints accessible to both roles (e.g. UserPreferences).
|
||||||
|
.AddPolicy(AuthPolicy.SenderOrReceiver, policy => policy
|
||||||
|
.AddAuthenticationSchemes(AuthScheme.Sender, AuthScheme.Receiver)
|
||||||
|
.RequireAuthenticatedUser());
|
||||||
|
|
||||||
// User Manager
|
// User Manager
|
||||||
#pragma warning disable CS0618
|
#pragma warning disable CS0618
|
||||||
|
|||||||
Reference in New Issue
Block a user