using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; using System.Collections.Generic; using Microsoft.AspNetCore.Http; using Oqtane.Shared; using Oqtane.Enums; using Oqtane.Infrastructure; using SZUAbsolventenverein.Module.PremiumArea.Services; using Oqtane.Controllers; using System.Net; using System.Threading.Tasks; using SZUAbsolventenverein.Module.PremiumArea.Models; using System.IO; using System; using Oqtane.Models; using Microsoft.AspNetCore.Hosting; using Oqtane.Repository; using Oqtane.Security; using System.Linq; using Oqtane.Managers; using SZUAbsolventenverein.Module.PremiumArea.Repository; namespace SZUAbsolventenverein.Module.PremiumArea.Controllers { [Route(ControllerRoutes.ApiRoute)] public class EngineerApplicationController : ModuleControllerBase { private readonly IEngineerApplicationService _service; private readonly IFileRepository _files; private readonly IFolderRepository _folders; private readonly IUserManager _users; private readonly IUserPremiumRepository _premiums; private readonly IHttpContextAccessor _accessor; private readonly IWebHostEnvironment _environment; public EngineerApplicationController(IEngineerApplicationService service, IFileRepository files, IFolderRepository folders, IUserManager users, IUserPremiumRepository premiums, ILogManager logger, IHttpContextAccessor accessor, IWebHostEnvironment environment) : base(logger, accessor) { _service = service; _files = files; _folders = folders; _users = users; _premiums = premiums; _accessor = accessor; _environment = environment; } // GET: api/?moduleid=x [HttpGet] [Authorize(Policy = PolicyNames.ViewModule)] public async Task> Get(string moduleid) { int ModuleId; if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId)) { return await _service.GetApplicationsAsync(ModuleId); } else { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication Get Attempt {ModuleId}", moduleid); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; return null; } } // GET: api//status/Approved?moduleid=x [HttpGet("status/{status}")] [Authorize(Policy = PolicyNames.ViewModule)] public async Task> GetByStatus(string status, string moduleid) { int ModuleId; if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId)) { return await _service.GetApplicationsAsync(ModuleId, status); } else { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication GetByStatus Attempt {ModuleId}", moduleid); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; return null; } } // GET api//5 [HttpGet("{id}")] [Authorize(Policy = PolicyNames.ViewModule)] public async Task Get(int id, string moduleid) { int ModuleId; if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId)) { return await _service.GetApplicationAsync(id, ModuleId); } else { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication Get Attempt {Id} {ModuleId}", id, moduleid); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; return null; } } // POST api/ [HttpPost] [Authorize(Policy = PolicyNames.ViewModule)] // Users can Create public async Task Post([FromBody] EngineerApplication Application) { if (ModelState.IsValid && IsAuthorizedEntityId(EntityNames.Module, Application.ModuleId)) { return await _service.AddApplicationAsync(Application); } else { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication Post Attempt {Application}", Application); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; return null; } } // PUT api//5 [HttpPut("{id}")] [Authorize(Policy = PolicyNames.ViewModule)] // Users can Edit own (Service checks ownership) public async Task Put(int id, [FromBody] EngineerApplication Application) { if (ModelState.IsValid && Application.ApplicationId == id && IsAuthorizedEntityId(EntityNames.Module, Application.ModuleId)) { return await _service.UpdateApplicationAsync(Application); } else { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication Put Attempt {Application}", Application); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; return null; } } // DELETE api//5 [HttpDelete("{id}")] [Authorize(Policy = PolicyNames.ViewModule)] public async Task Delete(int id, string moduleid) { int ModuleId; if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId)) { await _service.DeleteApplicationAsync(id, ModuleId); } else { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication Delete Attempt {Id} {ModuleId}", id, moduleid); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } } // POST api//approve/5 [HttpPost("approve/{id}")] [Authorize(Policy = PolicyNames.EditModule)] public async Task Approve(int id, string moduleid) { int ModuleId; if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId)) { // We need to clear IsReported flag as well. // Since the Service handles the logic, we should probably update it there. // But if I can't find it easily, I can do it here if I get the app first. // _service.ApproveApplicationAsync might just set Status="Approved". // I should verify where the service logic is. await _service.ApproveApplicationAsync(id, ModuleId); } else { HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } } [HttpPost("upload")] [Authorize(Policy = PolicyNames.ViewModule)] public async Task Upload(string moduleid) { int ModuleId; if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId)) { if (Request.Form.Files.Count == 0) return BadRequest("No file uploaded"); var file = Request.Form.Files[0]; if (file.ContentType != "application/pdf") return BadRequest("Only PDF files are allowed"); var alias = _accessor.HttpContext.Items["Alias"] as Alias; var siteId = alias.SiteId; var folderPath = "EngineerApplications"; var folder = _folders.GetFolder(siteId, folderPath); if (folder == null) { // Create folder folder = new Folder { SiteId = siteId, ParentId = null, Name = "EngineerApplications", Path = folderPath, PermissionList = new List { new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.Edit, RoleNames.Admin, true) } }; folder = _folders.AddFolder(folder); } var ext = Path.GetExtension(file.FileName).ToLower(); if (ext != ".pdf") return BadRequest("Invalid file extension"); var tenantId = alias.TenantId; var uploadPath = Path.Combine(_environment.ContentRootPath, "Content", "Tenants", tenantId.ToString(), "Sites", siteId.ToString(), folderPath); if (!Directory.Exists(uploadPath)) Directory.CreateDirectory(uploadPath); var uniqueName = $"{Guid.NewGuid()}{ext}"; var filePath = Path.Combine(uploadPath, uniqueName); using (var stream = new FileStream(filePath, FileMode.Create)) { await file.CopyToAsync(stream); } var fileObj = new Oqtane.Models.File { FolderId = folder.FolderId, Name = uniqueName, Extension = ext.Substring(1), Size = (int)file.Length, ImageHeight = 0, ImageWidth = 0 }; var addedFile = _files.AddFile(fileObj); return Ok(new { FileId = addedFile.FileId, FileName = file.FileName }); } return StatusCode((int)HttpStatusCode.Forbidden); } [HttpPost("report/{id}")] [Authorize(Policy = PolicyNames.ViewModule)] public async Task Report(int id, [FromBody] string reason, string moduleid) { int ModuleId; if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId)) { await _service.ReportApplicationAsync(id, ModuleId, reason); } else { HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } } [HttpGet("download/{id}")] [Authorize] public async Task Download(int id, string moduleid) { int ModuleId; if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId)) { var app = await _service.GetApplicationAsync(id, ModuleId); if (app == null) return NotFound(); var alias = _accessor.HttpContext.Items["Alias"] as Alias; // Access Rules: // 1. Admin if (_accessor.HttpContext.User.IsInRole(RoleNames.Admin)) { return await ServeFile(app.FileId.Value, app.PdfFileName); } // 2. Owner var username = _accessor.HttpContext.User.Identity.Name; var currentUserId = -1; if (username != null) { var u = _users.GetUser(username, alias.SiteId); if (u != null) currentUserId = u.UserId; } if (currentUserId == app.UserId) { return await ServeFile(app.FileId.Value, app.PdfFileName); } // 3. Premium User AND Published/Approved if (app.Status == "Approved" || app.Status == "Published") { var premium = _premiums.GetUserPremium(currentUserId); if (premium != null && premium.PremiumUntil.HasValue && premium.PremiumUntil.Value > DateTime.UtcNow) { return await ServeFile(app.FileId.Value, app.PdfFileName); } } return StatusCode((int)HttpStatusCode.Forbidden); } return StatusCode((int)HttpStatusCode.Forbidden); } private async Task ServeFile(int fileId, string downloadName) { var file = _files.GetFile(fileId); if (file != null) { var path = _files.GetFilePath(file); if (System.IO.File.Exists(path)) { var bytes = await System.IO.File.ReadAllBytesAsync(path); return File(bytes, "application/pdf", downloadName ?? file.Name); } } return NotFound(); } } }