578 lines
21 KiB
C#
578 lines
21 KiB
C#
using System;
|
|
using System.Net;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Options;
|
|
using Serilog;
|
|
using Landmark.OLA.Application.Common.Security;
|
|
using Landmark.OLA.Application.Configuration;
|
|
using Landmark.OLA.Application.DataAccess;
|
|
using Landmark.OLA.Application.DataAccessLayer;
|
|
using Landmark.OLA.Models.Loan;
|
|
using Landmark.OLA.Models.Loan.Enums;
|
|
using OnlineBankingAuth.Tokens;
|
|
|
|
namespace Landmark.OLA.Application.Controllers
|
|
{
|
|
[Route("api/[controller]")]
|
|
[ApiController]
|
|
public class StorageController : ControllerBase
|
|
{
|
|
|
|
private readonly ILoanApplicationDataAccess loanApplicationStorage;
|
|
private readonly ITokenService _tokenService;
|
|
private readonly BlobStorageSettings _blobStorageSettings;
|
|
private readonly IBlobStorageAccess _blobStorageAccess;
|
|
|
|
public StorageController(
|
|
ILoanApplicationDataAccess loanApplicationDataAccess,
|
|
ITokenService tokenService,
|
|
IOptions<BlobStorageSettings> blobStorageSettings,
|
|
IBlobStorageAccess blobStorageAccess)
|
|
{
|
|
loanApplicationStorage = loanApplicationDataAccess;
|
|
_tokenService = tokenService;
|
|
_blobStorageSettings = blobStorageSettings.Value;
|
|
_blobStorageAccess = blobStorageAccess;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use to retrieve a set of Loan Containers by filter
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Returns an array of Loan Containers given a filter
|
|
/// Cookies: None required or set.
|
|
/// Security: Must have a valid JWT token retrieved from Azure AD, with ADMIN role.
|
|
/// </remarks>
|
|
// Post: api/Storage/Filter
|
|
[HttpPost("Filter")]
|
|
[ProducesResponseType(typeof(IEnumerable<LoanContainer>), 200)]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(403)]
|
|
[ProducesResponseType(500)]
|
|
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Admin")]
|
|
public IActionResult Post([FromBody] Filter filter)
|
|
{
|
|
try
|
|
{
|
|
return Ok(loanApplicationStorage.GetLoanContainers(filter));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e.Message);
|
|
return StatusCode((int)System.Net.HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use to get a specific loan container FOR AN ADMIN
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Returns a loan container given a loan id.
|
|
/// Cookies: None required or set.
|
|
/// Security: Must have a valid JWT token retrieved from Azure AD.
|
|
/// </remarks>
|
|
// GET: api/Storage/5
|
|
[HttpGet("{id}", Name = "Get")]
|
|
[ProducesResponseType(typeof(LoanContainer), 200)]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(403)]
|
|
[ProducesResponseType(404)]
|
|
[ProducesResponseType(500)]
|
|
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
|
public IActionResult Get(string id)
|
|
{
|
|
try
|
|
{
|
|
if (String.IsNullOrEmpty(id))
|
|
{
|
|
return BadRequest("Id is required.");
|
|
}
|
|
LoanContainer container = loanApplicationStorage.GetContainerById(id);
|
|
return Ok(container);
|
|
}
|
|
catch (KeyNotFoundException ex)
|
|
{
|
|
Log.Error(ex.Message);
|
|
return NotFound("An application with that Id could not be found");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e.Message);
|
|
return StatusCode((int)System.Net.HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use to get loan containers for an authenticated member
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Returns a list of loan containers given a valid cookie with member id.
|
|
/// Cookies: Requires authenticated cookie with member id.
|
|
/// Security: None required or provided.
|
|
/// </remarks>
|
|
// GET: api/Storage
|
|
[HttpGet("GetMemberApplications", Name = "GetMemberApplications")]
|
|
[ProducesResponseType(typeof(List<LoanContainer>), 200)]
|
|
[ProducesResponseType(403)]
|
|
[ProducesResponseType(500)]
|
|
public IActionResult GetMemberApplications()
|
|
{
|
|
try
|
|
{
|
|
var assertion = _tokenService.GetAuthenticatedDataFromCookie(HttpContext);
|
|
if (string.IsNullOrEmpty(assertion?.MemberID) || string.IsNullOrEmpty(assertion?.IndividualID))
|
|
{
|
|
Log.Error(assertion.MemberID + "/" + assertion.IndividualID, "no stacktrace");
|
|
return StatusCode((int)System.Net.HttpStatusCode.Forbidden);
|
|
}
|
|
List<LoanContainer> containers = loanApplicationStorage.GetAllMemberContainers(assertion.MemberID, assertion.IndividualID);
|
|
return Ok(containers);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e.Message);
|
|
return StatusCode((int)System.Net.HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use to save a loan container
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Returns a saved loan container with id and possibly member id
|
|
/// Cookies: Authenticated member cookie optional, will cause id to be saved.
|
|
/// Security: Currently creates authentication and authorization cookies.
|
|
/// </remarks>
|
|
// POST: api/Storage
|
|
[HttpPost]
|
|
[ProducesResponseType(typeof(string), 201)]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(500)]
|
|
public object Post([FromBody] LoanContainer loanContainer)
|
|
{
|
|
try
|
|
{
|
|
if (loanContainer != null)
|
|
{
|
|
var token = _tokenService.GetAuthenticatedDataFromCookie(HttpContext);
|
|
loanContainer.Id = (Guid.NewGuid()).ToString();
|
|
loanContainer = MemberIdService.AppendMemberInfo(loanContainer, token);
|
|
loanContainer = ForwardedHeaderService.AppendIPAddress(loanContainer, HttpContext);
|
|
|
|
loanApplicationStorage.CreateContainer(loanContainer);
|
|
HttpContext.Response.StatusCode = 201;
|
|
return new { id = loanContainer.Id, message = "Successfully saved document" };
|
|
}
|
|
return BadRequest("Loan Application is required.");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e.Message);
|
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
[HttpPost("New")]
|
|
[ProducesResponseType(typeof(string), 201)]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(500)]
|
|
public object PostNew()
|
|
{
|
|
try
|
|
{
|
|
var token = _tokenService.GetAuthenticatedDataFromCookie(HttpContext);
|
|
var loanContainer = new LoanContainer(new LoanApplication())
|
|
{
|
|
Id = (Guid.NewGuid()).ToString()
|
|
};
|
|
loanContainer = MemberIdService.AppendMemberInfo(loanContainer, token);
|
|
loanContainer = ForwardedHeaderService.AppendIPAddress(loanContainer, HttpContext);
|
|
|
|
|
|
loanApplicationStorage.CreateContainer(loanContainer);
|
|
HttpContext.Response.StatusCode = 201;
|
|
return new { id = loanContainer.Id, message = "Successfully saved document" };
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e.Message);
|
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use update a loan container
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Returns a status after attempting to update a loan container with new info.
|
|
/// Cookies: None required or set.
|
|
/// Security: None required or provided.
|
|
/// </remarks>
|
|
// PUT: api/Storage
|
|
[HttpPut]
|
|
[ProducesResponseType(typeof(string), 200)]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(404)]
|
|
[ProducesResponseType(500)]
|
|
public IActionResult Put([FromBody] LoanContainer loanContainer)
|
|
{
|
|
try
|
|
{
|
|
var cosmosId = loanContainer?.Id;
|
|
if (!String.IsNullOrEmpty(cosmosId))
|
|
{
|
|
var assertion = _tokenService.GetAuthenticatedDataFromCookie(HttpContext);
|
|
loanContainer = CosmosIdService.AppendCosmosId(loanContainer, cosmosId);
|
|
loanContainer = MemberIdService.AppendMemberInfo(loanContainer, assertion);
|
|
loanContainer = ForwardedHeaderService.AppendIPAddress(loanContainer, HttpContext);
|
|
|
|
// Append files
|
|
LoanContainer previousContainer = loanApplicationStorage.GetContainerById(cosmosId);
|
|
if (previousContainer.LoanApplication != null) {
|
|
loanContainer.LoanApplication.Files = previousContainer.LoanApplication.Files;
|
|
}
|
|
|
|
loanApplicationStorage.UpdateContainer(loanContainer);
|
|
return CreatedAtAction(nameof(Post), new { id = loanContainer.Id }, loanContainer);
|
|
}
|
|
return BadRequest("Loan container invalid.");
|
|
}
|
|
catch (KeyNotFoundException ex)
|
|
{
|
|
Log.Error(ex.Message);
|
|
return NotFound();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
loanApplicationStorage.UpdateError(loanContainer.Id, new LoanError(LoanErrorCode.Error_Updating, e.Message));
|
|
Log.Error(e.Message);
|
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use to delete a loan
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Deletes a loan given an id.
|
|
/// Cookies: None required or set.
|
|
/// Security: Requires JWT token from Azure AD, AND Admin role.
|
|
/// </remarks>
|
|
// DELETE: api/Storage/Loan/5
|
|
[HttpDelete("Loan/{id}")]
|
|
[ProducesResponseType(200)]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(403)]
|
|
[ProducesResponseType(404)]
|
|
[ProducesResponseType(500)]
|
|
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Admin")]
|
|
public IActionResult DeleteLoan(string id)
|
|
{
|
|
try
|
|
{
|
|
if (String.IsNullOrEmpty(id))
|
|
{
|
|
return BadRequest("Id is required.");
|
|
}
|
|
loanApplicationStorage.DeleteApplication(id);
|
|
loanApplicationStorage.UpdateStatus(id, LoanStatus.Deleted);
|
|
return Ok(id);
|
|
}
|
|
catch (KeyNotFoundException ex)
|
|
{
|
|
Log.Error(ex.Message);
|
|
return NotFound();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
loanApplicationStorage.UpdateError(id, new LoanError(LoanErrorCode.Error_Deleting, e.Message));
|
|
Log.Error(e.Message);
|
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use to delete an entire loan container
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Deletes a loan container given a container id.
|
|
/// Cookies: None required or set.
|
|
/// Security: Requires JWT token from Azure AD, AND Admin role.
|
|
/// </remarks>
|
|
// DELETE: api/Storage/LoanContainer/5
|
|
[HttpDelete("LoanContainer/{id}")]
|
|
[ProducesResponseType(200)]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(403)]
|
|
[ProducesResponseType(404)]
|
|
[ProducesResponseType(500)]
|
|
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Admin")]
|
|
public IActionResult DeleteContainer(string id)
|
|
{
|
|
try
|
|
{
|
|
if (String.IsNullOrEmpty(id))
|
|
{
|
|
return BadRequest("Id is required.");
|
|
}
|
|
loanApplicationStorage.DeleteContainer(id);
|
|
return Ok(id);
|
|
}
|
|
catch (KeyNotFoundException ex)
|
|
{
|
|
Log.Error(ex.Message);
|
|
return NotFound();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e.Message);
|
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
// GET: api/Storage/Settings
|
|
[HttpGet("Settings")]
|
|
[ProducesResponseType(typeof(string), 200)]
|
|
[ProducesResponseType(500)]
|
|
public IActionResult GetSettings()
|
|
{
|
|
try
|
|
{
|
|
return Ok(new { fileSizeLimit = _blobStorageSettings.MaxFileSizeInMB, fileLimit = _blobStorageSettings.FileLimit, allowedFileTypes = _blobStorageSettings.AllowedFileTypes });
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e.Message);
|
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
// GET: api/Storage/Documents
|
|
[HttpGet("Documents/{cosmosLoanId}")]
|
|
[ProducesResponseType(typeof(string), 200)]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(404)]
|
|
[ProducesResponseType(500)]
|
|
public IActionResult GetDocuments(string cosmosLoanId)
|
|
{
|
|
try
|
|
{
|
|
if (String.IsNullOrEmpty(cosmosLoanId))
|
|
{
|
|
return BadRequest(new { error = "Invalid or missing loan ID" });
|
|
}
|
|
|
|
LoanContainer container = loanApplicationStorage.GetContainerById(cosmosLoanId);
|
|
|
|
if(container.LoanApplication == null)
|
|
{
|
|
return NotFound(new { loanId = cosmosLoanId });
|
|
}
|
|
|
|
return Ok(new { files = container.LoanApplication.Files });
|
|
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e.Message);
|
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
// POST: api/Storage/Documents/5
|
|
[HttpPost("Documents/{cosmosLoanId}")]
|
|
[ProducesResponseType(typeof(string), 201)]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(404)]
|
|
[ProducesResponseType(500)]
|
|
public IActionResult PostDocument(string cosmosLoanId, [FromForm] DocumentUploadFromLoanApplication request)
|
|
{
|
|
try
|
|
{
|
|
if (String.IsNullOrEmpty(cosmosLoanId))
|
|
{
|
|
return BadRequest(new { error = "Invalid or missing loan ID" });
|
|
}
|
|
|
|
LoanContainer container = loanApplicationStorage.GetContainerById(cosmosLoanId);
|
|
|
|
if (container.LoanApplication == null)
|
|
{
|
|
return NotFound(new { loanId = cosmosLoanId });
|
|
}
|
|
|
|
if (DocumentUploadFromLoanApplication.IsNullOrEmpty(request))
|
|
{
|
|
return BadRequest(new { error = "Cannot upload empty file" });
|
|
}
|
|
|
|
using var ms = new MemoryStream();
|
|
// Check file type
|
|
var filename = request.File.FileName.ToLower();
|
|
|
|
var validExtension = false;
|
|
|
|
foreach (string extension in _blobStorageSettings.AllowedFileTypes)
|
|
{
|
|
if (filename.EndsWith(extension))
|
|
{
|
|
validExtension = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!validExtension)
|
|
{
|
|
return BadRequest(new { error = "Invalid file type" });
|
|
}
|
|
|
|
// Check file size
|
|
if (request.File.Length > _blobStorageSettings.MaxFileSizeInMB * 1024 * 1024)
|
|
{
|
|
return BadRequest(new { error = $"File size exceeds {_blobStorageSettings.MaxFileSizeInMB} MB" });
|
|
}
|
|
|
|
request.File.CopyTo(ms);
|
|
|
|
var fileBytes = ms.ToArray();
|
|
|
|
var fileId = Guid.NewGuid();
|
|
|
|
LoanApplicationFile file = new(cosmosLoanId, fileId, request.File.FileName, request.Section, request.SectionId, request.Description);
|
|
|
|
container.LoanApplication.Files.Add(file);
|
|
|
|
loanApplicationStorage.UpdateContainer(container);
|
|
|
|
_blobStorageAccess.SaveBlob(file, fileBytes);
|
|
|
|
return Created($"/api/Storage/Documents/{cosmosLoanId}/{fileId}", new { fileId });
|
|
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e.Message);
|
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
// DELETE: api/Storage/Documents/1/5
|
|
[HttpDelete("Documents/{cosmosLoanId}/{fileId}")]
|
|
[ProducesResponseType(typeof(string), 200)]
|
|
[ProducesResponseType(typeof(string), 400)]
|
|
[ProducesResponseType(typeof(string), 404)]
|
|
[ProducesResponseType(500)]
|
|
public IActionResult DeleteDocument(string cosmosLoanId, string fileId)
|
|
{
|
|
try
|
|
{
|
|
if (String.IsNullOrEmpty(cosmosLoanId) || String.IsNullOrEmpty(fileId))
|
|
{
|
|
return BadRequest(new { error = "Invalid or missing loan or file ID" });
|
|
}
|
|
|
|
LoanContainer container = loanApplicationStorage.GetContainerById(cosmosLoanId);
|
|
|
|
if (container.LoanApplication == null)
|
|
{
|
|
return NotFound(new { loanId = cosmosLoanId });
|
|
}
|
|
|
|
foreach (LoanApplicationFile file in container.LoanApplication.Files)
|
|
{
|
|
if(file.FileId.ToString().Equals(fileId, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
_blobStorageAccess.DeleteBlob(file);
|
|
|
|
container.LoanApplication.Files.Remove(file);
|
|
|
|
loanApplicationStorage.UpdateContainer(container);
|
|
|
|
return Ok(new { message = "file deleted", fileId });
|
|
}
|
|
}
|
|
|
|
return NotFound(new { fileId });
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e.Message);
|
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
// DELETE: api/Storage/Documents/1/5
|
|
[HttpPost("Documents/DeleteAll/{cosmosLoanId}")]
|
|
[ProducesResponseType(typeof(string), 200)]
|
|
[ProducesResponseType(typeof(string), 400)]
|
|
[ProducesResponseType(typeof(string), 404)]
|
|
[ProducesResponseType(500)]
|
|
public IActionResult DeleteDocuments(string cosmosLoanId, [FromForm] string files)
|
|
{
|
|
try
|
|
{
|
|
/*if (String.IsNullOrEmpty(cosmosLoanId) || String.IsNullOrEmpty(fileIds))
|
|
{
|
|
return BadRequest(new { error = "Invalid or missing loan or file ID" });
|
|
}*/
|
|
|
|
LoanContainer container = loanApplicationStorage.GetContainerById(cosmosLoanId);
|
|
|
|
if (container.LoanApplication == null)
|
|
{
|
|
return NotFound(new { loanId = cosmosLoanId });
|
|
}
|
|
List<LoanApplicationFile> filesDeleted = new List<LoanApplicationFile>();
|
|
foreach (LoanApplicationFile file in container.LoanApplication.Files)
|
|
{
|
|
|
|
if (files.ToString().Contains(file.FileId.ToString()))
|
|
{
|
|
_blobStorageAccess.DeleteBlob(file);
|
|
|
|
filesDeleted.Add(file);
|
|
}
|
|
}
|
|
|
|
|
|
foreach (LoanApplicationFile file in filesDeleted)
|
|
{
|
|
container.LoanApplication.Files.Remove(file);
|
|
}
|
|
|
|
loanApplicationStorage.UpdateContainer(container);
|
|
|
|
return Ok(new { message = "files deleted", filesDeleted });
|
|
|
|
/*return NotFound(new { fileId });*/
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e.Message);
|
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
[HttpGet("/GetLog")]
|
|
[ProducesResponseType(typeof(string), 200)]
|
|
public IActionResult GetLog()
|
|
{
|
|
try
|
|
{
|
|
Log.Error("Test log error from localhost");
|
|
return Ok("Logging to Seq");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e.Message);
|
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
}
|
|
}
|