Compare commits
15 Commits
6327fef2e0
...
a8a73724e6
| Author | SHA1 | Date | |
|---|---|---|---|
| a8a73724e6 | |||
| 8059e0aae4 | |||
| 4c5116695b | |||
| ffa31fbebc | |||
| e536e523b8 | |||
| 1d64c19605 | |||
| 29e6ba8733 | |||
| 2e6eeafd74 | |||
| 643501f484 | |||
| 0c900d219c | |||
| 8d8757810c | |||
| 9dec681ce5 | |||
| 01ec2b3df2 | |||
| d64d46920a | |||
| 6147a619a8 |
@@ -42,13 +42,21 @@ namespace EnvelopeGenerator.Domain.Entities
|
||||
|
||||
[Column("ADDED_WHEN")]
|
||||
[Required]
|
||||
public DateTime AddedWhen { get; set; }
|
||||
public DateTime
|
||||
#if NET
|
||||
?
|
||||
#endif
|
||||
AddedWhen { get; set; }
|
||||
|
||||
[Column("CHANGED_WHO")]
|
||||
[StringLength(100)]
|
||||
public string ChangedWho { get; set; }
|
||||
public string
|
||||
#if NET
|
||||
?
|
||||
#endif
|
||||
ChangedWho { get; set; }
|
||||
|
||||
[Column("CHANGED_WHEN")]
|
||||
public DateTime ChangedWhen { get; set; }
|
||||
public DateTime? ChangedWhen { get; set; }
|
||||
}
|
||||
}
|
||||
406
EnvelopeGenerator.Web/Controllers/EnvelopeController.cs
Normal file
406
EnvelopeGenerator.Web/Controllers/EnvelopeController.cs
Normal file
@@ -0,0 +1,406 @@
|
||||
using DigitalData.Core.Abstraction.Application.DTO;
|
||||
using DigitalData.Core.API;
|
||||
using DigitalData.Core.Client;
|
||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
||||
using EnvelopeGenerator.Application.Resources;
|
||||
using EnvelopeGenerator.Web.Extensions;
|
||||
using EnvelopeGenerator.Web.Models;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Newtonsoft.Json;
|
||||
using OtpNet;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
using EnvelopeGenerator.Application.Common.Dto;
|
||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||
using EnvelopeGenerator.Application.Common.Extensions;
|
||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||
|
||||
namespace EnvelopeGenerator.Web.Controllers;
|
||||
|
||||
[Route("Envelope")]
|
||||
public class EnvelopeController : ViewControllerBase
|
||||
{
|
||||
[Obsolete("Use MediatR")]
|
||||
private readonly IEnvelopeReceiverService _envRcvService;
|
||||
[Obsolete("Use MediatR")]
|
||||
private readonly IEnvelopeHistoryService _historyService;
|
||||
private readonly IConfiguration _configuration;
|
||||
[Obsolete("Use MediatR")]
|
||||
private readonly IEnvelopeMailService _mailService;
|
||||
[Obsolete("Use MediatR")]
|
||||
private readonly IEnvelopeReceiverReadOnlyService _readOnlyService;
|
||||
private readonly IAuthenticator _authenticator;
|
||||
[Obsolete("Use MediatR")]
|
||||
private readonly IReceiverService _rcvService;
|
||||
private readonly IEnvelopeSmsHandler _envSmsHandler;
|
||||
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
[Obsolete("Use MediatR")]
|
||||
public EnvelopeController(ILogger<EnvelopeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer<Resource> localizer, IConfiguration configuration, Cultures cultures, IEnvelopeMailService envelopeMailService, IEnvelopeReceiverReadOnlyService readOnlyService, IAuthenticator authenticator, IReceiverService receiverService, IEnvelopeSmsHandler envelopeSmsService, IMediator mediator) : base(logger, cultures, localizer)
|
||||
{
|
||||
_envRcvService = envelopeReceiverService;
|
||||
_historyService = historyService;
|
||||
_configuration = configuration;
|
||||
_mailService = envelopeMailService;
|
||||
_readOnlyService = readOnlyService;
|
||||
_authenticator = authenticator;
|
||||
_rcvService = receiverService;
|
||||
_envSmsHandler = envelopeSmsService;
|
||||
_mediator = mediator;
|
||||
}
|
||||
|
||||
[HttpGet("{envelopeReceiverId}")]
|
||||
[Obsolete("Use MediatR")]
|
||||
public async Task<IActionResult> Main([FromRoute] string envelopeReceiverId, CancellationToken cancel)
|
||||
{
|
||||
try
|
||||
{
|
||||
ViewData["EnvelopeKey"] = envelopeReceiverId;
|
||||
|
||||
if (!envelopeReceiverId.TryDecode(out var decoded))
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return this.ViewDocumentNotFound();
|
||||
}
|
||||
|
||||
#region Read Only
|
||||
if (decoded.GetEncodeType() == EncodeType.EnvelopeReceiverReadOnly)
|
||||
return await EnvelopeReceiverReadOnly(decoded.ParseReadOnlyId());
|
||||
#endregion
|
||||
|
||||
var er = await _mediator.ReadEnvelopeReceiverAsync(envelopeReceiverId, cancel);
|
||||
|
||||
if (er is null)
|
||||
return this.ViewEnvelopeNotFound();
|
||||
|
||||
#region Rejected or Signed
|
||||
//check rejection
|
||||
var rejRcvrs = await _historyService.ReadRejectingReceivers(er.Envelope!.Id);
|
||||
if (rejRcvrs.Any())
|
||||
{
|
||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
ViewBag.IsExt = !rejRcvrs.Contains(er.Receiver); //external if the current user is not rejected
|
||||
return View("EnvelopeRejected", er);
|
||||
}
|
||||
|
||||
//check if it has already signed
|
||||
if (await _historyService.IsSigned(envelopeId: er.Envelope!.Id, userReference: er.Receiver!.EmailAddress))
|
||||
{
|
||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
return View("EnvelopeSigned");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Send Access Code
|
||||
bool accessCodeAlreadyRequested = await _historyService.AccessCodeAlreadyRequested(envelopeId: er.Envelope!.Id, userReference: er.Receiver!.EmailAddress);
|
||||
if (!accessCodeAlreadyRequested)
|
||||
{
|
||||
await _historyService.RecordAsync(er.EnvelopeId, er.Receiver.EmailAddress, EnvelopeStatus.AccessCodeRequested);
|
||||
|
||||
var mailRes = await _mailService.SendAccessCodeAsync(envelopeReceiverDto: er);
|
||||
if (mailRes.IsFailed)
|
||||
{
|
||||
_logger.LogNotice(mailRes);
|
||||
return this.ViewAccessCodeNotSent();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
return await CreateEnvelopeLockedView(er, cancel);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception:ex, message: _localizer.UnexpectedError());
|
||||
return this.ViewInnerServiceError();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("{envelopeReceiverId}")]
|
||||
[Obsolete("Use MediatR")]
|
||||
public async Task<IActionResult> LogInEnvelope([FromRoute] string envelopeReceiverId, [FromForm] Auth auth)
|
||||
{
|
||||
try
|
||||
{
|
||||
ViewData["EnvelopeKey"] = envelopeReceiverId;
|
||||
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
|
||||
|
||||
if (uuid is null || signature is null)
|
||||
{
|
||||
_logger.LogEnvelopeError(uuid: uuid, signature: signature, message: _localizer.WrongEnvelopeReceiverId());
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
_logger.LogInformation("Envelope UUID: [{uuid}]\nReceiver Signature: [{signature}]", uuid, signature);
|
||||
|
||||
var er_secret_res = await _envRcvService.ReadWithSecretByUuidSignatureAsync(uuid: uuid, signature: signature);
|
||||
|
||||
if (er_secret_res.IsFailed)
|
||||
{
|
||||
_logger.LogNotice(er_secret_res.Notices);
|
||||
return this.ViewEnvelopeNotFound();
|
||||
}
|
||||
var er_secret = er_secret_res.Data;
|
||||
|
||||
// show envelope if already logged in
|
||||
if (User.IsInRole(ReceiverRole.FullyAuth))
|
||||
return await CreateShowEnvelopeView(er_secret);
|
||||
|
||||
if (auth.HasMulti)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
else if (auth.HasAccessCode)
|
||||
{
|
||||
if (await HandleAccessCodeAsync(auth, er_secret, envelopeReceiverId) is IActionResult acView)
|
||||
return acView;
|
||||
}
|
||||
else if (auth.HasSmsCode)
|
||||
{
|
||||
if (await HandleSmsAsync(auth, er_secret, envelopeReceiverId) is IActionResult smsView)
|
||||
return smsView;
|
||||
}
|
||||
else if (auth.HasAuthenticatorCode)
|
||||
{
|
||||
if (await HandleAuthenticatorAsync(auth, er_secret, envelopeReceiverId) is IActionResult aView)
|
||||
return aView;
|
||||
}
|
||||
else
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return View("EnvelopeLocked")
|
||||
.WithData("EnvelopeKey", envelopeReceiverId)
|
||||
.WithData("TFAEnabled", er_secret.Envelope!.TFAEnabled)
|
||||
.WithData("HasPhoneNumber", er_secret.HasPhoneNumber)
|
||||
.WithData("SenderEmail", er_secret.Envelope.User!.Email)
|
||||
.WithData("EnvelopeTitle", er_secret.Envelope.Title)
|
||||
.WithData("ErrorMessage", _localizer.WrongEnvelopeReceiverId());
|
||||
}
|
||||
|
||||
await HttpContext.SignInEnvelopeAsync(er_secret, ReceiverRole.FullyAuth);
|
||||
|
||||
return await CreateShowEnvelopeView(er_secret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception: ex);
|
||||
return this.ViewInnerServiceError();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IActionResult> CreateEnvelopeLockedView(EnvelopeReceiverDto er, CancellationToken cancel)
|
||||
{
|
||||
var uuidClaim = User.GetAuthEnvelopeUuid();
|
||||
var signatureClaim = User.GetAuthReceiverSignature();
|
||||
if (uuidClaim is not null
|
||||
&& uuidClaim == er.Envelope?.Uuid
|
||||
&& signatureClaim is not null
|
||||
&& signatureClaim == er.Receiver?.Signature
|
||||
&& User.IsInRole(ReceiverRole.FullyAuth))
|
||||
{
|
||||
if (er.Envelope!.Documents?.FirstOrDefault() is DocumentDto doc && doc.ByteData is not null)
|
||||
{
|
||||
ViewData["DocumentBytes"] = doc.ByteData;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogEnvelopeError(uuid: er.Envelope.Uuid, er.Receiver?.Signature, message: "No document byte-data was found in ENVELOPE_DOCUMENT table.");
|
||||
return this.ViewDocumentNotFound();
|
||||
}
|
||||
|
||||
await HttpContext.SignInEnvelopeAsync(er, ReceiverRole.FullyAuth);
|
||||
|
||||
//add PSPDFKit licence key
|
||||
ViewData["PSPDFKitLicenseKey"] = _configuration["PSPDFKitLicenseKey"];
|
||||
|
||||
return View("ShowEnvelope", er);
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewData["TFAEnabled"] = er.Envelope!.TFAEnabled;
|
||||
ViewData["HasPhoneNumber"] = er.HasPhoneNumber;
|
||||
ViewData["SenderEmail"] = er.Envelope.User!.Email;
|
||||
ViewData["EnvelopeTitle"] = er.Envelope.Title;
|
||||
return View("EnvelopeLocked");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IActionResult> CreateShowEnvelopeView(EnvelopeReceiverDto er)
|
||||
{
|
||||
if (er.Envelope!.Documents?.FirstOrDefault() is DocumentDto doc && doc.ByteData is not null)
|
||||
{
|
||||
ViewData["DocumentBytes"] = doc.ByteData;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogEnvelopeError(er.Envelope.Uuid, er.Receiver?.Signature, message: "No document byte-data was found in ENVELOPE_DOCUMENT table.");
|
||||
return this.ViewDocumentNotFound();
|
||||
}
|
||||
|
||||
await HttpContext.SignInEnvelopeAsync(er, ReceiverRole.FullyAuth);
|
||||
|
||||
//add PSPDFKit licence key
|
||||
ViewData["PSPDFKitLicenseKey"] = _configuration["PSPDFKitLicenseKey"];
|
||||
|
||||
return View("ShowEnvelope", er);
|
||||
}
|
||||
|
||||
#region TFA Views
|
||||
[NonAction]
|
||||
private async Task<IActionResult> TFAViewAsync(bool viaSms, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
|
||||
{
|
||||
if (viaSms)
|
||||
{
|
||||
var (smsRes, expiration) = await _envSmsHandler.SendTotpAsync(er_secret);
|
||||
|
||||
ViewData["EnvelopeKey"] = envelopeReceiverId;
|
||||
ViewData["TFAEnabled"] = er_secret.Envelope!.TFAEnabled;
|
||||
ViewData["HasPhoneNumber"] = er_secret.HasPhoneNumber;
|
||||
ViewData["SenderEmail"] = er_secret.Envelope.User!.Email;
|
||||
ViewData["EnvelopeTitle"] = er_secret.Envelope.Title;
|
||||
|
||||
if (smsRes?.Failed ?? false)
|
||||
{
|
||||
var res_json = JsonConvert.SerializeObject(smsRes);
|
||||
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, message: $"An unexpected error occurred while sending an SMS code. Response: ${res_json}");
|
||||
return this.ViewInnerServiceError();
|
||||
}
|
||||
|
||||
return View("EnvelopeLocked").WithData("CodeType", "smsCode").WithData("SmsExpiration", expiration);
|
||||
}
|
||||
else
|
||||
{
|
||||
return View("EnvelopeLocked")
|
||||
.WithData("CodeType", "authenticatorCode")
|
||||
.WithData("TfaRegDeadline", er_secret.Receiver?.TfaRegDeadline);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use MediatR")]
|
||||
[NonAction]
|
||||
private async Task<IActionResult?> HandleAccessCodeAsync(Auth auth, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
|
||||
{
|
||||
//check the access code verification
|
||||
if (er_secret.AccessCode != auth.AccessCode)
|
||||
{
|
||||
//EnvelopeStatusQuery.AccessCodeIncorrect
|
||||
await _historyService.RecordAsync(er_secret.EnvelopeId, er_secret.Receiver!.EmailAddress, EnvelopeStatus.AccessCodeIncorrect);
|
||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return View("EnvelopeLocked")
|
||||
.WithData("EnvelopeKey", envelopeReceiverId)
|
||||
.WithData("TFAEnabled", er_secret.Envelope!.TFAEnabled)
|
||||
.WithData("HasPhoneNumber", er_secret.HasPhoneNumber)
|
||||
.WithData("SenderEmail", er_secret.Envelope.User!.Email)
|
||||
.WithData("EnvelopeTitle", er_secret.Envelope.Title)
|
||||
.WithData("ErrorMessage", _localizer.WrongAccessCode());
|
||||
}
|
||||
|
||||
await _historyService.RecordAsync(er_secret.EnvelopeId, er_secret.Receiver!.EmailAddress, EnvelopeStatus.AccessCodeCorrect);
|
||||
|
||||
//check if the user has phone is added
|
||||
if (er_secret.Envelope!.TFAEnabled)
|
||||
{
|
||||
var rcv = er_secret.Receiver;
|
||||
if (rcv.TotpSecretkey is null)
|
||||
{
|
||||
rcv.TotpSecretkey = _authenticator.GenerateTotpSecretKey();
|
||||
await _rcvService.UpdateAsync(rcv);
|
||||
}
|
||||
|
||||
await HttpContext.SignInEnvelopeAsync(er_secret, ReceiverRole.PreAuth);
|
||||
|
||||
return await TFAViewAsync(auth.UserSelectSMS, er_secret, envelopeReceiverId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
private async Task<IActionResult?> HandleSmsAsync(Auth auth, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
|
||||
{
|
||||
if (er_secret.Receiver!.TotpSecretkey is null)
|
||||
throw new InvalidOperationException($"TotpSecretkey of DTO cannot validate without TotpSecretkey. Dto: {JsonConvert.SerializeObject(er_secret)}");
|
||||
|
||||
if (!User.IsInRole(ReceiverRole.PreAuth) || !_envSmsHandler.VerifyTotp(auth.SmsCode!, er_secret.Receiver.TotpSecretkey))
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
ViewData["ErrorMessage"] = _localizer.WrongAccessCode();
|
||||
return await TFAViewAsync(viaSms: true, er_secret, envelopeReceiverId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
private async Task<IActionResult?> HandleAuthenticatorAsync(Auth auth, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
|
||||
{
|
||||
if (er_secret.Receiver!.TotpSecretkey is null)
|
||||
throw new InvalidOperationException($"TotpSecretkey of DTO cannot validate without TotpSecretkey. Dto: {JsonConvert.SerializeObject(er_secret)}");
|
||||
|
||||
if (!User.IsInRole(ReceiverRole.PreAuth) || !_authenticator.VerifyTotp(auth.AuthenticatorCode!, er_secret.Receiver.TotpSecretkey, window: VerificationWindow.RfcSpecifiedNetworkDelay))
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
ViewData["ErrorMessage"] = _localizer.WrongAccessCode();
|
||||
return await TFAViewAsync(viaSms: false, er_secret, envelopeReceiverId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
[NonAction]
|
||||
[Obsolete("Use MediatR")]
|
||||
public async Task<IActionResult> EnvelopeReceiverReadOnly([FromRoute] long readOnlyId)
|
||||
{
|
||||
var erro_res = await _readOnlyService.ReadByIdAsync(readOnlyId);
|
||||
if (erro_res.IsFailed)
|
||||
{
|
||||
_logger.LogNotice(erro_res.Notices);
|
||||
return this.ViewInnerServiceError();
|
||||
}
|
||||
|
||||
var erro = erro_res.Data;
|
||||
|
||||
if (DateTime.Now > erro.DateValid)
|
||||
return View("EnvelopeExpired");
|
||||
|
||||
return await _envRcvService.ReadByUuidSignatureAsync(uuid: erro.Envelope!.Uuid, erro.Receiver!.Signature).ThenAsync(
|
||||
SuccessAsync: async er =>
|
||||
{
|
||||
var envelopeKey = (er.Envelope!.Uuid, er.Receiver!.Signature).ToEnvelopeKey();
|
||||
|
||||
//TODO: implement multi-threading to history process (Task)
|
||||
var hist_res = await _historyService.RecordAsync((int)erro.EnvelopeId, erro.AddedWho, EnvelopeStatus.EnvelopeViewed);
|
||||
if (hist_res.IsFailed)
|
||||
{
|
||||
_logger.LogError(
|
||||
"Although the envelope was sent as read-only, the EnvelopeShared hisotry could not be saved. ReadOnly-Id: {readOnlyKey}\nEnvelope Receiver:\n{envelopeReceiver}",
|
||||
readOnlyId, JsonConvert.SerializeObject(er));
|
||||
_logger.LogNotice(hist_res.Notices);
|
||||
}
|
||||
|
||||
if (er.Envelope.Documents?.FirstOrDefault() is DocumentDto doc && doc.ByteData is not null)
|
||||
{
|
||||
ViewData["DocumentBytes"] = doc.ByteData;
|
||||
ViewData["EnvelopeKey"] = envelopeKey;
|
||||
ViewData["IsReadOnly"] = true;
|
||||
ViewData["ReadOnly"] = erro;
|
||||
ViewData["PSPDFKitLicenseKey"] = _configuration["PSPDFKitLicenseKey"];
|
||||
return View("ShowEnvelope", er);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogEnvelopeError(envelopeReceiverId: envelopeKey, message: "No document byte-data was found in ENVELOPE_DOCUMENT table.");
|
||||
return this.ViewDocumentNotFound();
|
||||
}
|
||||
},
|
||||
Fail: (messages, notices) =>
|
||||
{
|
||||
_logger.LogNotice(notices);
|
||||
return this.ViewEnvelopeNotFound();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,60 +1,22 @@
|
||||
using DigitalData.Core.Abstraction.Application.DTO;
|
||||
using DigitalData.Core.API;
|
||||
using DigitalData.Core.Client;
|
||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
||||
using EnvelopeGenerator.Application.Resources;
|
||||
using EnvelopeGenerator.Application.Resources;
|
||||
using EnvelopeGenerator.Web.Extensions;
|
||||
using EnvelopeGenerator.Web.Models;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Newtonsoft.Json;
|
||||
using OtpNet;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
using EnvelopeGenerator.Application.Common.Dto;
|
||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||
using EnvelopeGenerator.Application.Common.Extensions;
|
||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||
|
||||
namespace EnvelopeGenerator.Web.Controllers;
|
||||
|
||||
[Route("Envelope")]
|
||||
[Route("/")]
|
||||
public class HomeController : ViewControllerBase
|
||||
{
|
||||
[Obsolete("Use MediatR")]
|
||||
private readonly IEnvelopeReceiverService _envRcvService;
|
||||
[Obsolete("Use MediatR")]
|
||||
private readonly IEnvelopeHistoryService _historyService;
|
||||
private readonly IConfiguration _configuration;
|
||||
[Obsolete("Use MediatR")]
|
||||
private readonly IEnvelopeMailService _mailService;
|
||||
[Obsolete("Use MediatR")]
|
||||
private readonly IEnvelopeReceiverReadOnlyService _readOnlyService;
|
||||
private readonly IAuthenticator _authenticator;
|
||||
[Obsolete("Use MediatR")]
|
||||
private readonly IReceiverService _rcvService;
|
||||
private readonly IEnvelopeSmsHandler _envSmsHandler;
|
||||
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
[Obsolete("Use MediatR")]
|
||||
public HomeController(ILogger<HomeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer<Resource> localizer, IConfiguration configuration, Cultures cultures, IEnvelopeMailService envelopeMailService, IEnvelopeReceiverReadOnlyService readOnlyService, IAuthenticator authenticator, IReceiverService receiverService, IEnvelopeSmsHandler envelopeSmsService, IMediator mediator) : base(logger, cultures, localizer)
|
||||
public HomeController(IConfiguration configuration, ILogger<HomeController> logger, Cultures cultures, IStringLocalizer<Resource> localizer) : base(logger, cultures, localizer)
|
||||
{
|
||||
_envRcvService = envelopeReceiverService;
|
||||
_historyService = historyService;
|
||||
_configuration = configuration;
|
||||
_mailService = envelopeMailService;
|
||||
_readOnlyService = readOnlyService;
|
||||
_authenticator = authenticator;
|
||||
_rcvService = receiverService;
|
||||
_envSmsHandler = envelopeSmsService;
|
||||
_mediator = mediator;
|
||||
}
|
||||
|
||||
[HttpGet("/")]
|
||||
[HttpGet]
|
||||
public IActionResult Main()
|
||||
{
|
||||
return View(new MainViewModel()
|
||||
@@ -63,367 +25,6 @@ public class HomeController : ViewControllerBase
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("{envelopeReceiverId}")]
|
||||
[Obsolete("Use MediatR")]
|
||||
public async Task<IActionResult> MainAsync([FromRoute] string envelopeReceiverId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!envelopeReceiverId.TryDecode(out var decoded))
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return this.ViewDocumentNotFound();
|
||||
}
|
||||
|
||||
if(decoded.GetEncodeType() == EncodeType.EnvelopeReceiverReadOnly)
|
||||
return await EnvelopeReceiverReadOnly(decoded.ParseReadOnlyId());
|
||||
|
||||
ViewData["EnvelopeKey"] = envelopeReceiverId;
|
||||
|
||||
var er = await _mediator.ReadEnvelopeReceiverAsync(envelopeReceiverId);
|
||||
|
||||
if (er is null)
|
||||
return this.ViewEnvelopeNotFound();
|
||||
|
||||
bool accessCodeAlreadyRequested = await _historyService.AccessCodeAlreadyRequested(envelopeId: er.Envelope!.Id, userReference: er.Receiver!.EmailAddress);
|
||||
if (!accessCodeAlreadyRequested)
|
||||
{
|
||||
await _historyService.RecordAsync(er.EnvelopeId, er.Receiver.EmailAddress, EnvelopeStatus.AccessCodeRequested);
|
||||
|
||||
var mailRes = await _mailService.SendAccessCodeAsync(envelopeReceiverDto: er);
|
||||
if (mailRes.IsFailed)
|
||||
{
|
||||
_logger.LogNotice(mailRes);
|
||||
return this.ViewAccessCodeNotSent();
|
||||
}
|
||||
}
|
||||
|
||||
return Redirect($"{envelopeReceiverId}/Locked");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception:ex, message: _localizer.UnexpectedError());
|
||||
return this.ViewInnerServiceError();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{envelopeReceiverId}/Locked")]
|
||||
[Obsolete("Use DigitalData.Core.Exceptions and .Middleware")]
|
||||
public async Task<IActionResult> EnvelopeLocked([FromRoute] string envelopeReceiverId, CancellationToken cancel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var er = await _mediator.ReadEnvelopeReceiverAsync(envelopeReceiverId, cancel);
|
||||
|
||||
if (er is null)
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return this.ViewEnvelopeNotFound();
|
||||
}
|
||||
|
||||
if (User.IsInRole(ReceiverRole.FullyAuth))
|
||||
return await CreateShowEnvelopeView(envelopeReceiverId, er);
|
||||
else
|
||||
{
|
||||
ViewData["EnvelopeKey"] = envelopeReceiverId;
|
||||
ViewData["TFAEnabled"] = er.Envelope!.TFAEnabled;
|
||||
ViewData["HasPhoneNumber"] = er.HasPhoneNumber;
|
||||
ViewData["SenderEmail"] = er.Envelope.User!.Email;
|
||||
ViewData["EnvelopeTitle"] = er.Envelope.Title;
|
||||
return View();
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception: ex);
|
||||
return this.ViewInnerServiceError();
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use MediatR")]
|
||||
private async Task<IActionResult> CreateShowEnvelopeView(string envelopeReceiverId, EnvelopeReceiverDto er)
|
||||
{
|
||||
try
|
||||
{
|
||||
ViewData["EnvelopeKey"] = envelopeReceiverId;
|
||||
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
|
||||
|
||||
if (uuid is null || signature is null)
|
||||
{
|
||||
_logger.LogEnvelopeError(uuid: uuid, signature: signature, message: _localizer.WrongEnvelopeReceiverId());
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
_logger.LogInformation("Envelope UUID: [{uuid}]\nReceiver Signature: [{signature}]", uuid, signature);
|
||||
|
||||
//check rejection
|
||||
var rejRcvrs = await _historyService.ReadRejectingReceivers(er.Envelope!.Id);
|
||||
if (rejRcvrs.Any())
|
||||
{
|
||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
ViewBag.IsExt = !rejRcvrs.Contains(er.Receiver); //external if the current user is not rejected
|
||||
return View("EnvelopeRejected", er);
|
||||
}
|
||||
|
||||
//check if it has already signed
|
||||
if (await _historyService.IsSigned(envelopeId: er.Envelope!.Id, userReference: er.Receiver!.EmailAddress))
|
||||
{
|
||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
return View("EnvelopeSigned");
|
||||
}
|
||||
|
||||
if (er.Envelope.Documents?.FirstOrDefault() is DocumentDto doc && doc.ByteData is not null)
|
||||
{
|
||||
ViewData["DocumentBytes"] = doc.ByteData;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, message: "No document byte-data was found in ENVELOPE_DOCUMENT table.");
|
||||
return this.ViewDocumentNotFound();
|
||||
}
|
||||
|
||||
await HttpContext.SignInEnvelopeAsync(er, ReceiverRole.FullyAuth);
|
||||
|
||||
//add PSPDFKit licence key
|
||||
ViewData["PSPDFKitLicenseKey"] = _configuration["PSPDFKitLicenseKey"];
|
||||
|
||||
return View("ShowEnvelope", er);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception: ex);
|
||||
return this.ViewInnerServiceError();
|
||||
}
|
||||
}
|
||||
|
||||
#region TFA Views
|
||||
[NonAction]
|
||||
private async Task<IActionResult> TFAViewAsync(bool viaSms, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
|
||||
{
|
||||
if (viaSms)
|
||||
{
|
||||
var (smsRes, expiration) = await _envSmsHandler.SendTotpAsync(er_secret);
|
||||
|
||||
ViewData["EnvelopeKey"] = envelopeReceiverId;
|
||||
ViewData["TFAEnabled"] = er_secret.Envelope!.TFAEnabled;
|
||||
ViewData["HasPhoneNumber"] = er_secret.HasPhoneNumber;
|
||||
ViewData["SenderEmail"] = er_secret.Envelope.User!.Email;
|
||||
ViewData["EnvelopeTitle"] = er_secret.Envelope.Title;
|
||||
|
||||
if (smsRes?.Failed ?? false)
|
||||
{
|
||||
var res_json = JsonConvert.SerializeObject(smsRes);
|
||||
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, message: $"An unexpected error occurred while sending an SMS code. Response: ${res_json}");
|
||||
return this.ViewInnerServiceError();
|
||||
}
|
||||
|
||||
return View("EnvelopeLocked").WithData("CodeType", "smsCode").WithData("SmsExpiration", expiration);
|
||||
}
|
||||
else
|
||||
{
|
||||
return View("EnvelopeLocked")
|
||||
.WithData("CodeType", "authenticatorCode")
|
||||
.WithData("TfaRegDeadline", er_secret.Receiver?.TfaRegDeadline);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use MediatR")]
|
||||
[NonAction]
|
||||
private async Task<IActionResult?> HandleAccessCodeAsync(Auth auth, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
|
||||
{
|
||||
//check the access code verification
|
||||
if (er_secret.AccessCode != auth.AccessCode)
|
||||
{
|
||||
//EnvelopeStatusQuery.AccessCodeIncorrect
|
||||
await _historyService.RecordAsync(er_secret.EnvelopeId, er_secret.Receiver!.EmailAddress, EnvelopeStatus.AccessCodeIncorrect);
|
||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return View("EnvelopeLocked")
|
||||
.WithData("EnvelopeKey", envelopeReceiverId)
|
||||
.WithData("TFAEnabled", er_secret.Envelope!.TFAEnabled)
|
||||
.WithData("HasPhoneNumber", er_secret.HasPhoneNumber)
|
||||
.WithData("SenderEmail", er_secret.Envelope.User!.Email)
|
||||
.WithData("EnvelopeTitle", er_secret.Envelope.Title)
|
||||
.WithData("ErrorMessage", _localizer.WrongAccessCode());
|
||||
}
|
||||
|
||||
await _historyService.RecordAsync(er_secret.EnvelopeId, er_secret.Receiver!.EmailAddress, EnvelopeStatus.AccessCodeCorrect);
|
||||
|
||||
//check if the user has phone is added
|
||||
if (er_secret.Envelope!.TFAEnabled)
|
||||
{
|
||||
var rcv = er_secret.Receiver;
|
||||
if (rcv.TotpSecretkey is null)
|
||||
{
|
||||
rcv.TotpSecretkey = _authenticator.GenerateTotpSecretKey();
|
||||
await _rcvService.UpdateAsync(rcv);
|
||||
}
|
||||
|
||||
await HttpContext.SignInEnvelopeAsync(er_secret, ReceiverRole.PreAuth);
|
||||
|
||||
return await TFAViewAsync(auth.UserSelectSMS, er_secret, envelopeReceiverId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
private async Task<IActionResult?> HandleSmsAsync(Auth auth, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
|
||||
{
|
||||
if (er_secret.Receiver!.TotpSecretkey is null)
|
||||
throw new InvalidOperationException($"TotpSecretkey of DTO cannot validate without TotpSecretkey. Dto: {JsonConvert.SerializeObject(er_secret)}");
|
||||
|
||||
if (!User.IsInRole(ReceiverRole.PreAuth) || !_envSmsHandler.VerifyTotp(auth.SmsCode!, er_secret.Receiver.TotpSecretkey))
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
ViewData["ErrorMessage"] = _localizer.WrongAccessCode();
|
||||
return await TFAViewAsync(viaSms: true, er_secret, envelopeReceiverId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
private async Task<IActionResult?> HandleAuthenticatorAsync(Auth auth, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
|
||||
{
|
||||
if (er_secret.Receiver!.TotpSecretkey is null)
|
||||
throw new InvalidOperationException($"TotpSecretkey of DTO cannot validate without TotpSecretkey. Dto: {JsonConvert.SerializeObject(er_secret)}");
|
||||
|
||||
if (!User.IsInRole(ReceiverRole.PreAuth) || !_authenticator.VerifyTotp(auth.AuthenticatorCode!, er_secret.Receiver.TotpSecretkey, window: VerificationWindow.RfcSpecifiedNetworkDelay))
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
ViewData["ErrorMessage"] = _localizer.WrongAccessCode();
|
||||
return await TFAViewAsync(viaSms: false, er_secret, envelopeReceiverId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
[HttpPost("{envelopeReceiverId}/Locked")]
|
||||
[Obsolete("Use MediatR")]
|
||||
public async Task<IActionResult> LogInEnvelope([FromRoute] string envelopeReceiverId, [FromForm] Auth auth)
|
||||
{
|
||||
try
|
||||
{
|
||||
ViewData["EnvelopeKey"] = envelopeReceiverId;
|
||||
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
|
||||
|
||||
if (uuid is null || signature is null)
|
||||
{
|
||||
_logger.LogEnvelopeError(uuid: uuid, signature: signature, message: _localizer.WrongEnvelopeReceiverId());
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
_logger.LogInformation("Envelope UUID: [{uuid}]\nReceiver Signature: [{signature}]", uuid, signature);
|
||||
|
||||
var er_secret_res = await _envRcvService.ReadWithSecretByUuidSignatureAsync(uuid: uuid, signature: signature);
|
||||
|
||||
if (er_secret_res.IsFailed)
|
||||
{
|
||||
_logger.LogNotice(er_secret_res.Notices);
|
||||
return this.ViewEnvelopeNotFound();
|
||||
}
|
||||
var er_secret = er_secret_res.Data;
|
||||
|
||||
// show envelope if already logged in
|
||||
if (User.IsInRole(ReceiverRole.FullyAuth))
|
||||
return await CreateShowEnvelopeView(envelopeReceiverId, er_secret);
|
||||
|
||||
if (auth.HasMulti)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
else if (auth.HasAccessCode)
|
||||
{
|
||||
if (await HandleAccessCodeAsync(auth, er_secret, envelopeReceiverId) is IActionResult acView)
|
||||
return acView;
|
||||
}
|
||||
else if (auth.HasSmsCode)
|
||||
{
|
||||
if (await HandleSmsAsync(auth, er_secret, envelopeReceiverId) is IActionResult smsView)
|
||||
return smsView;
|
||||
}
|
||||
else if (auth.HasAuthenticatorCode)
|
||||
{
|
||||
if(await HandleAuthenticatorAsync(auth, er_secret, envelopeReceiverId) is IActionResult aView)
|
||||
return aView;
|
||||
}
|
||||
else
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return View("EnvelopeLocked")
|
||||
.WithData("EnvelopeKey", envelopeReceiverId)
|
||||
.WithData("TFAEnabled", er_secret.Envelope!.TFAEnabled)
|
||||
.WithData("HasPhoneNumber", er_secret.HasPhoneNumber)
|
||||
.WithData("SenderEmail", er_secret.Envelope.User!.Email)
|
||||
.WithData("EnvelopeTitle", er_secret.Envelope.Title)
|
||||
.WithData("ErrorMessage", _localizer.WrongEnvelopeReceiverId());
|
||||
}
|
||||
|
||||
await HttpContext.SignInEnvelopeAsync(er_secret, ReceiverRole.FullyAuth);
|
||||
|
||||
return await CreateShowEnvelopeView(envelopeReceiverId, er_secret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception: ex);
|
||||
return this.ViewInnerServiceError();
|
||||
}
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
[Obsolete("Use MediatR")]
|
||||
public async Task<IActionResult> EnvelopeReceiverReadOnly([FromRoute] long readOnlyId)
|
||||
{
|
||||
var erro_res = await _readOnlyService.ReadByIdAsync(readOnlyId);
|
||||
if (erro_res.IsFailed)
|
||||
{
|
||||
_logger.LogNotice(erro_res.Notices);
|
||||
return this.ViewInnerServiceError();
|
||||
}
|
||||
|
||||
var erro = erro_res.Data;
|
||||
|
||||
if (DateTime.Now > erro.DateValid)
|
||||
return View("EnvelopeExpired");
|
||||
|
||||
return await _envRcvService.ReadByUuidSignatureAsync(uuid: erro.Envelope!.Uuid, erro.Receiver!.Signature).ThenAsync(
|
||||
SuccessAsync: async er =>
|
||||
{
|
||||
var envelopeKey = (er.Envelope!.Uuid, er.Receiver!.Signature).ToEnvelopeKey();
|
||||
|
||||
//TODO: implement multi-threading to history process (Task)
|
||||
var hist_res = await _historyService.RecordAsync((int)erro.EnvelopeId, erro.AddedWho, EnvelopeStatus.EnvelopeViewed);
|
||||
if (hist_res.IsFailed)
|
||||
{
|
||||
_logger.LogError(
|
||||
"Although the envelope was sent as read-only, the EnvelopeShared hisotry could not be saved. ReadOnly-Id: {readOnlyKey}\nEnvelope Receiver:\n{envelopeReceiver}",
|
||||
readOnlyId, JsonConvert.SerializeObject(er));
|
||||
_logger.LogNotice(hist_res.Notices);
|
||||
}
|
||||
|
||||
if (er.Envelope.Documents?.FirstOrDefault() is DocumentDto doc && doc.ByteData is not null)
|
||||
{
|
||||
ViewData["DocumentBytes"] = doc.ByteData;
|
||||
ViewData["EnvelopeKey"] = envelopeKey;
|
||||
ViewData["IsReadOnly"] = true;
|
||||
ViewData["ReadOnly"] = erro;
|
||||
ViewData["PSPDFKitLicenseKey"] = _configuration["PSPDFKitLicenseKey"];
|
||||
return View("ShowEnvelope", er);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogEnvelopeError(envelopeReceiverId: envelopeKey, message: "No document byte-data was found in ENVELOPE_DOCUMENT table.");
|
||||
return this.ViewDocumentNotFound();
|
||||
}
|
||||
},
|
||||
Fail: (messages, notices) =>
|
||||
{
|
||||
_logger.LogNotice(notices);
|
||||
return this.ViewEnvelopeNotFound();
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("Error404")]
|
||||
public IActionResult Error404() => this.ViewError404();
|
||||
}
|
||||
@@ -12,9 +12,9 @@
|
||||
<PackageTags>digital data envelope generator web</PackageTags>
|
||||
<Description>EnvelopeGenerator.Web is an ASP.NET MVC application developed to manage signing processes. It uses Entity Framework Core (EF Core) for database operations. The user interface for signing processes is developed with Razor View Engine (.cshtml files) and JavaScript under wwwroot, integrated with PSPDFKit. This integration allows users to view and sign documents seamlessly.</Description>
|
||||
<ApplicationIcon>Assets\icon.ico</ApplicationIcon>
|
||||
<Version>3.3.0</Version>
|
||||
<AssemblyVersion>3.3.0</AssemblyVersion>
|
||||
<FileVersion>3.3.0</FileVersion>
|
||||
<Version>3.3.1</Version>
|
||||
<AssemblyVersion>3.3.1</AssemblyVersion>
|
||||
<FileVersion>3.3.1</FileVersion>
|
||||
<Copyright>Copyright © 2025 Digital Data GmbH. All rights reserved.</Copyright>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ try
|
||||
{
|
||||
// Dynamically calculate the redirection path, for example:
|
||||
var envelopeReceiverId = context.HttpContext.Request.RouteValues["envelopeReceiverId"];
|
||||
context.RedirectUri = $"/EnvelopeKey/{envelopeReceiverId}/Locked";
|
||||
context.RedirectUri = $"/EnvelopeKey/{envelopeReceiverId}";
|
||||
|
||||
context.Response.Redirect(context.RedirectUri);
|
||||
return Task.CompletedTask;
|
||||
@@ -145,7 +145,7 @@ try
|
||||
{
|
||||
// Apply a similar redirection logic for logout
|
||||
var envelopeReceiverId = context.HttpContext.Request.RouteValues["envelopeReceiverId"];
|
||||
context.RedirectUri = $"/EnvelopeKey/{envelopeReceiverId}/Success";
|
||||
context.RedirectUri = $"/EnvelopeKey/{envelopeReceiverId}";
|
||||
|
||||
context.Response.Redirect(context.RedirectUri);
|
||||
return Task.CompletedTask;
|
||||
|
||||
@@ -6,7 +6,7 @@ class Content {
|
||||
|
||||
class API {
|
||||
static get REJECT_URL() {
|
||||
return `/api/envelope/reject`;
|
||||
return `/api/annotation/reject`;
|
||||
}
|
||||
|
||||
static get REJECT_REDIR_URL() {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
class Content{static get JSON(){return"application/json"}}class API{static get REJECT_URL(){return`/api/envelope/reject`}static get REJECT_REDIR_URL(){return`/envelope/${API.ENV_KEY}`}static get SHARE_URL(){return`/api/readonly`}static __XSRF_TOKEN
|
||||
class Content{static get JSON(){return"application/json"}}class API{static get REJECT_URL(){return`/api/annotation/reject`}static get REJECT_REDIR_URL(){return`/envelope/${API.ENV_KEY}`}static get SHARE_URL(){return`/api/readonly`}static __XSRF_TOKEN
|
||||
static get XSRF_TOKEN(){return API.__XSRF_TOKEN??=document.getElementsByName("__RequestVerificationToken")[0].value,API.__XSRF_TOKEN}static get ENV_KEY(){return ENV_KEY??document.querySelector('meta[name="env-key"]').getAttribute("content")}}const submitForm=async n=>await fetch(n.action,{method:n.method,body:new FormData(n),headers:{"X-Requested-With":"XMLHttpRequest"}}),createRequest=async(n,t,i,r)=>fetch(t,{credentials:"include",method:n,headers:{"Content-Type":r,"X-XSRF-TOKEN":API.XSRF_TOKEN},body:JSON.stringify(i)}),createPost=(n,t,i)=>createRequest("POST",n,t,i),rejectEnvelope=n=>createPost(API.REJECT_URL,n,Content.JSON),redirect=n=>window.location.href=n,redirRejected=()=>redirect(API.REJECT_REDIR_URL),shareEnvelope=(n,t)=>createPost(API.SHARE_URL,{receiverMail:n,dateValid:t},Content.JSON);
|
||||
Reference in New Issue
Block a user