using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using ProposalSystem.Application.DTOs; using ProposalSystem.Application.Interfaces; using ProposalSystem.Domain.Entities; using ProposalSystem.Infrastructure.Data; namespace ProposalSystem.Api.Controllers; [ApiController] [Route("api/generated-pdfs")] [Authorize] public class GeneratedPdfsController : ControllerBase { private readonly ProposalDbContext _db; private readonly ICurrentUserService _currentUser; public GeneratedPdfsController(ProposalDbContext db, ICurrentUserService currentUser) { _db = db; _currentUser = currentUser; } [HttpPost] [Authorize(Roles = "admins,sysadmins")] public async Task Create([FromBody] CreateGeneratedPdfRequest request, CancellationToken ct) { var proposal = await _db.Proposals.FindAsync(new object[] { request.ProposalId }, ct); if (proposal == null) return NotFound(); // Fix: API-M4 — verify dispatcher ownership before allowing PDF creation if (!AuthorizeProposalAccess(proposal)) return Forbid(); await _currentUser.ResolveAsync(); var pdf = new GeneratedPdf { Id = Guid.NewGuid(), ProposalId = request.ProposalId, Revision = proposal.CurrentRevision, S3Key = request.S3Key, GeneratedAt = DateTime.UtcNow, GeneratedById = _currentUser.UserId, }; _db.GeneratedPdfs.Add(pdf); await _db.SaveChangesAsync(ct); return Created($"/api/generated-pdfs/{pdf.Id}", new { pdf.Id, pdf.S3Key, pdf.Revision }); } // Fix: API-M4 — dispatchers can only access PDFs for proposals they submitted private bool AuthorizeProposalAccess(Proposal proposal) { if (_currentUser.Role != UserRole.Dispatcher) return true; return proposal.SubmittedById == _currentUser.UserId; } }