Add signup/signin with backend-issued JWT (Argon2 passwords in SurrealDB), role-based authorization policies, per-user data access levels, consent register and read audit log. Protect user endpoints and record sensitive reads.
99 lines
2.7 KiB
C#
99 lines
2.7 KiB
C#
using Asp.Versioning;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using TarninaHub.Application.Abstractions;
|
|
using TarninaHub.Application.Dtos;
|
|
using TarninaHub.Application.Security;
|
|
|
|
namespace TarninaHub.Controllers;
|
|
|
|
[ApiController]
|
|
[ApiVersion("1.0")]
|
|
[Route("api/consents")]
|
|
[Authorize]
|
|
public class ConsentsController : ControllerBase
|
|
{
|
|
private readonly IConsentService _consentService;
|
|
private readonly IAuditLogService _auditLogService;
|
|
|
|
public ConsentsController(IConsentService consentService, IAuditLogService auditLogService)
|
|
{
|
|
_consentService = consentService;
|
|
_auditLogService = auditLogService;
|
|
}
|
|
|
|
[HttpPost]
|
|
public async Task<ActionResult<ConsentResponse>> Grant(
|
|
GrantConsentRequest request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var userId = User.GetUserId();
|
|
|
|
if (string.IsNullOrWhiteSpace(userId))
|
|
{
|
|
return Unauthorized();
|
|
}
|
|
|
|
if (!ConsentScopes.All.Contains(request.Scope))
|
|
{
|
|
ModelState.AddModelError(nameof(request.Scope),
|
|
$"Scope must be one of: {string.Join(", ", ConsentScopes.All)}.");
|
|
|
|
return ValidationProblem(ModelState);
|
|
}
|
|
|
|
var consent = await _consentService.GrantAsync(userId, request, cancellationToken);
|
|
|
|
await _auditLogService.RecordAsync(
|
|
userId,
|
|
"consent.grant",
|
|
consent.Id,
|
|
new Dictionary<string, object?> { ["scope"] = request.Scope },
|
|
cancellationToken);
|
|
|
|
return StatusCode(StatusCodes.Status201Created, consent);
|
|
}
|
|
|
|
[HttpGet("mine")]
|
|
public async Task<ActionResult<IReadOnlyList<ConsentResponse>>> GetMine(CancellationToken cancellationToken)
|
|
{
|
|
var userId = User.GetUserId();
|
|
|
|
if (string.IsNullOrWhiteSpace(userId))
|
|
{
|
|
return Unauthorized();
|
|
}
|
|
|
|
var consents = await _consentService.ListForUserAsync(userId, cancellationToken);
|
|
|
|
return Ok(consents);
|
|
}
|
|
|
|
[HttpPost("{consentId}/revoke")]
|
|
public async Task<IActionResult> Revoke(string consentId, CancellationToken cancellationToken)
|
|
{
|
|
var userId = User.GetUserId();
|
|
|
|
if (string.IsNullOrWhiteSpace(userId))
|
|
{
|
|
return Unauthorized();
|
|
}
|
|
|
|
var revoked = await _consentService.RevokeAsync(consentId, userId, cancellationToken);
|
|
|
|
if (!revoked)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
await _auditLogService.RecordAsync(
|
|
userId,
|
|
"consent.revoke",
|
|
consentId,
|
|
new Dictionary<string, object?>(),
|
|
cancellationToken);
|
|
|
|
return NoContent();
|
|
}
|
|
}
|