Compare commits
8 Commits
refactor/a
...
feat/locat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46f1633f79 | ||
|
|
1af07f0df8 | ||
|
|
55c9dfb9c2 | ||
|
|
1a230306a3 | ||
|
|
28022bc669 | ||
|
|
ad5843b7c9 | ||
|
|
173b691e56 | ||
|
|
8a0fe70a88 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -363,5 +363,3 @@ MigrationBackup/
|
|||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
/EnvelopeGenerator.Web/.config/dotnet-tools.json
|
/EnvelopeGenerator.Web/.config/dotnet-tools.json
|
||||||
/EnvelopeGenerator.GeneratorAPI/ClientApp/envelope-generator-ui/.vscode
|
/EnvelopeGenerator.GeneratorAPI/ClientApp/envelope-generator-ui/.vscode
|
||||||
/EnvelopeGenerator.Tests.Application/Services/BugFixTests.cs
|
|
||||||
/EnvelopeGenerator.Tests.Application/annotations.json
|
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.DTO;
|
|
||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
using EnvelopeGenerator.Application.Common.Notifications.DocSigned;
|
|
||||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
|
||||||
using EnvelopeGenerator.Application.Histories.Queries;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.API.Extensions;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Manages annotations and signature lifecycle for envelopes.
|
|
||||||
/// </summary>
|
|
||||||
[Authorize(Roles = Role.Receiver.FullyAuth)]
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
public class AnnotationController : ControllerBase
|
|
||||||
{
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
private readonly IEnvelopeHistoryService _historyService;
|
|
||||||
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
private readonly IEnvelopeReceiverService _envelopeReceiverService;
|
|
||||||
|
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
private readonly ILogger<AnnotationController> _logger;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of <see cref="AnnotationController"/>.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public AnnotationController(
|
|
||||||
ILogger<AnnotationController> logger,
|
|
||||||
IEnvelopeHistoryService envelopeHistoryService,
|
|
||||||
IEnvelopeReceiverService envelopeReceiverService,
|
|
||||||
IMediator mediator)
|
|
||||||
{
|
|
||||||
_historyService = envelopeHistoryService;
|
|
||||||
_envelopeReceiverService = envelopeReceiverService;
|
|
||||||
_mediator = mediator;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates or updates annotations for the authenticated envelope receiver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="psPdfKitAnnotation">Annotation payload.</param>
|
|
||||||
/// <param name="cancel">Cancellation token.</param>
|
|
||||||
[Authorize(Roles = Role.Receiver.FullyAuth)]
|
|
||||||
[HttpPost]
|
|
||||||
[Obsolete("PSPDF Kit will no longer be used.")]
|
|
||||||
public async Task<IActionResult> CreateOrUpdate([FromBody] PsPdfKitAnnotation? psPdfKitAnnotation = null, CancellationToken cancel = default)
|
|
||||||
{
|
|
||||||
var signature = User.GetReceiverSignatureOfReceiver();
|
|
||||||
var uuid = User.GetEnvelopeUuidOfReceiver();
|
|
||||||
|
|
||||||
var envelopeReceiver = await _mediator.ReadEnvelopeReceiverAsync(uuid, signature, cancel).ThrowIfNull(Exceptions.NotFound);
|
|
||||||
|
|
||||||
if (!envelopeReceiver.Envelope!.ReadOnly && psPdfKitAnnotation is null)
|
|
||||||
return BadRequest();
|
|
||||||
|
|
||||||
if (await _mediator.IsSignedAsync(uuid, signature, cancel))
|
|
||||||
return Problem(statusCode: StatusCodes.Status409Conflict);
|
|
||||||
else if (await _mediator.AnyHistoryAsync(uuid, new[] { EnvelopeStatus.EnvelopeRejected, EnvelopeStatus.DocumentRejected }, cancel))
|
|
||||||
return Problem(statusCode: StatusCodes.Status423Locked);
|
|
||||||
|
|
||||||
var docSignedNotification = await _mediator
|
|
||||||
.ReadEnvelopeReceiverAsync(uuid, signature, cancel)
|
|
||||||
.ToDocSignedNotification(psPdfKitAnnotation)
|
|
||||||
?? throw new NotFoundException("Envelope receiver is not found.");
|
|
||||||
|
|
||||||
await _mediator.PublishSafely(docSignedNotification, cancel);
|
|
||||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
|
||||||
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Rejects the document for the current receiver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="reason">Optional rejection reason.</param>
|
|
||||||
[Authorize(Roles = Role.Receiver.FullyAuth)]
|
|
||||||
[HttpPost("reject")]
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public async Task<IActionResult> Reject([FromBody] string? reason = null)
|
|
||||||
{
|
|
||||||
var signature = User.GetReceiverSignatureOfReceiver();
|
|
||||||
var uuid = User.GetEnvelopeUuidOfReceiver();
|
|
||||||
var mail = User.GetReceiverMailOfReceiver();
|
|
||||||
|
|
||||||
var envRcvRes = await _envelopeReceiverService.ReadByUuidSignatureAsync(uuid: uuid, signature: signature);
|
|
||||||
|
|
||||||
if (envRcvRes.IsFailed)
|
|
||||||
{
|
|
||||||
_logger.LogNotice(envRcvRes.Notices);
|
|
||||||
return Unauthorized("you are not authorized");
|
|
||||||
}
|
|
||||||
|
|
||||||
var histRes = await _historyService.RecordAsync(envRcvRes.Data.EnvelopeId, userReference: mail, EnvelopeStatus.DocumentRejected, comment: reason);
|
|
||||||
if (histRes.IsSuccess)
|
|
||||||
{
|
|
||||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogEnvelopeError(uuid: uuid, signature: signature, message: "Unexpected error happened in api/envelope/reject");
|
|
||||||
_logger.LogNotice(histRes.Notices);
|
|
||||||
return StatusCode(500, histRes.Messages);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using EnvelopeGenerator.API.Models;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Controller verantwortlich für die Benutzer-Authentifizierung, einschließlich Anmelden, Abmelden und Überprüfung des Authentifizierungsstatus.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger"></param>
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
[ApiController]
|
|
||||||
public partial class AuthController(ILogger<AuthController> logger) : ControllerBase
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Entfernt das Authentifizierungs-Cookie des Benutzers (AuthCookie)
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// Gibt eine HTTP 200 oder 401.
|
|
||||||
/// </returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// Sample request:
|
|
||||||
///
|
|
||||||
/// POST /api/auth/logout
|
|
||||||
///
|
|
||||||
/// </remarks>
|
|
||||||
/// <response code="200">Erfolgreich gelöscht, wenn der Benutzer ein berechtigtes Cookie hat.</response>
|
|
||||||
/// <response code="401">Wenn es kein zugelassenes Cookie gibt, wird „nicht zugelassen“ zurückgegeben.</response>
|
|
||||||
[ProducesResponseType(typeof(string), StatusCodes.Status200OK, "text/javascript")]
|
|
||||||
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
|
||||||
[Authorize]
|
|
||||||
[HttpPost("logout")]
|
|
||||||
public async Task<IActionResult> Logout()
|
|
||||||
{
|
|
||||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Prüft, ob der Benutzer ein autorisiertes Token hat.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Wenn ein autorisiertes Token vorhanden ist HTTP 200 asynchron 401</returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// Sample request:
|
|
||||||
///
|
|
||||||
/// GET /api/auth
|
|
||||||
///
|
|
||||||
/// </remarks>
|
|
||||||
/// <response code="200">Wenn es einen autorisierten Cookie gibt.</response>
|
|
||||||
/// <response code="401">Wenn kein Cookie vorhanden ist oder nicht autorisierte.</response>
|
|
||||||
[ProducesResponseType(typeof(string), StatusCodes.Status200OK, "text/javascript")]
|
|
||||||
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
|
||||||
[Authorize]
|
|
||||||
[HttpGet]
|
|
||||||
public IActionResult IsAuthenticated() => Ok();
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Exposes configuration data required by the client applications.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Initializes a new instance of <see cref="ConfigController"/>.
|
|
||||||
/// </remarks>
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class ConfigController(IOptionsMonitor<AnnotationParams> annotationParamsOptions) : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly AnnotationParams _annotationParams = annotationParamsOptions.CurrentValue;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns annotation configuration that was previously rendered by MVC.
|
|
||||||
/// </summary>
|
|
||||||
[HttpGet("Annotations")]
|
|
||||||
[Obsolete("PSPDF Kit will no longer be used.")]
|
|
||||||
public IActionResult GetAnnotationParams()
|
|
||||||
{
|
|
||||||
return Ok(_annotationParams.AnnotationJSObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
using EnvelopeGenerator.API.Extensions;
|
|
||||||
using EnvelopeGenerator.Application.Documents.Queries;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides access to envelope documents for authenticated receivers.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Initializes a new instance of the <see cref="DocumentController"/> class.
|
|
||||||
/// </remarks>
|
|
||||||
[Authorize]
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
public class DocumentController(IMediator mediator, ILogger<DocumentController> logger) : ControllerBase
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the document bytes receiver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="query">Encoded envelope key.</param>
|
|
||||||
/// <param name="cancel">Cancellation token.</param>
|
|
||||||
[HttpGet]
|
|
||||||
[Authorize(Roles = $"{Role.Sender},{Role.Receiver.FullyAuth}")]
|
|
||||||
public async Task<IActionResult> GetDocument(CancellationToken cancel, [FromQuery] ReadDocumentQuery? query = null)
|
|
||||||
{
|
|
||||||
// Sender: expects query with envelope key
|
|
||||||
if (User.IsInRole(Role.Sender))
|
|
||||||
{
|
|
||||||
if (query is null)
|
|
||||||
return BadRequest("Missing document query.");
|
|
||||||
|
|
||||||
var senderDoc = await mediator.Send(query, cancel);
|
|
||||||
return senderDoc.ByteData is byte[] senderDocByte
|
|
||||||
? File(senderDocByte, "application/octet-stream")
|
|
||||||
: NotFound("Document is empty.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Receiver: resolve envelope id from claims
|
|
||||||
if (User.IsInRole(Role.Receiver.FullyAuth))
|
|
||||||
{
|
|
||||||
if (query is not null)
|
|
||||||
return BadRequest("Query parameters are not allowed for receiver role.");
|
|
||||||
|
|
||||||
var envelopeId = User.GetEnvelopeIdOfReceiver();
|
|
||||||
var receiverDoc = await mediator.Send(new ReadDocumentQuery { EnvelopeId = envelopeId }, cancel);
|
|
||||||
return receiverDoc.ByteData is byte[] receiverDocByte
|
|
||||||
? File(receiverDocByte, "application/octet-stream")
|
|
||||||
: NotFound("Document is empty.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Unauthorized();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using EnvelopeGenerator.Application.EmailTemplates;
|
|
||||||
using EnvelopeGenerator.Application.EmailTemplates.Commands.Update;
|
|
||||||
using EnvelopeGenerator.Application.EmailTemplates.Commands.Reset;
|
|
||||||
using EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using MediatR;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Controller for managing temp templates.
|
|
||||||
/// Steuerung zur Verwaltung von E-Mail-Vorlagen.
|
|
||||||
/// </summary>
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class EmailTemplateController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IMapper _mapper;
|
|
||||||
|
|
||||||
private readonly IRepository<EmailTemplate> _repository;
|
|
||||||
|
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialisiert eine neue Instanz der <see cref="EmailTemplateController"/>-Klasse.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mapper">
|
|
||||||
/// <param name="repository">
|
|
||||||
/// Die AutoMapper-Instanz, die zum Zuordnen von Objekten verwendet wird.
|
|
||||||
/// </param>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public EmailTemplateController(IMapper mapper, IRepository<EmailTemplate> repository, IMediator mediator)
|
|
||||||
{
|
|
||||||
_mapper = mapper;
|
|
||||||
_repository = repository;
|
|
||||||
_mediator = mediator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ruft E-Mail-Vorlagen basierend auf der angegebenen Abfrage ab.
|
|
||||||
/// Gibt alles zurück, wenn keine Id- oder Typ-Informationen eingegeben wurden.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="emailTemplate">Die Abfrageparameter zum Abrufen von E-Mail-Vorlagen.</param>
|
|
||||||
/// <returns>Gibt HTTP-Antwort zurück</returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// Sample request:
|
|
||||||
/// GET /api/EmailTemplate?emailTemplateId=123
|
|
||||||
/// </remarks>
|
|
||||||
/// <response code="200">Wenn die E-Mail-Vorlagen erfolgreich abgerufen werden.</response>
|
|
||||||
/// <response code="400">Wenn die Abfrageparameter ungültig sind.</response>
|
|
||||||
/// <response code="401">Wenn der Benutzer nicht authentifiziert ist.</response>
|
|
||||||
/// <response code="404">Wenn die gesuchte Abfrage nicht gefunden wird.</response>
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> Get([FromQuery] ReadEmailTemplateQuery? emailTemplate = null)
|
|
||||||
{
|
|
||||||
if (emailTemplate is null || (emailTemplate.Id is null && emailTemplate.Type is null))
|
|
||||||
{
|
|
||||||
var temps = await _repository.Query.ToListAsync();
|
|
||||||
return Ok(_mapper.Map<IEnumerable<EmailTemplateDto>>(temps));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var temp = await _mediator.Send(emailTemplate);
|
|
||||||
return temp is null ? NotFound() : Ok(temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates an temp template or resets it if no update command is provided.
|
|
||||||
/// Aktualisiert eine E-Mail-Vorlage oder setzt sie zurück, wenn kein Aktualisierungsbefehl angegeben ist.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="temp">Die E-Mail-Vorlagenabfrage.</param>
|
|
||||||
/// <param name="update">Der Aktualisierungsbefehl für die E-Mail-Vorlage.
|
|
||||||
/// Wird auf Standardwert aktualisiert, wenn die Anfrage ohne http-Body gesendet wird.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>Gibt HTTP-Antwort zurück</returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// Sample request:
|
|
||||||
/// PUT /api/EmailTemplate
|
|
||||||
/// {
|
|
||||||
/// "emailTemplateId": 123,
|
|
||||||
/// "newContent": "Updated content"
|
|
||||||
/// }
|
|
||||||
/// </remarks>
|
|
||||||
/// <response code="200">Wenn die E-Mail-Vorlage erfolgreich aktualisiert oder zurückgesetzt wird.</response>
|
|
||||||
/// <response code="400">Wenn die Abfrage ohne einen String gesendet wird.</response>
|
|
||||||
/// <response code="401">Wenn der Benutzer nicht authentifiziert ist.</response>
|
|
||||||
/// <response code="404">Wenn die gesuchte Abfrage nicht gefunden wird.</response>
|
|
||||||
[HttpPut]
|
|
||||||
public async Task<IActionResult> Update([FromQuery] EmailTemplateQuery? temp = null, [FromBody] UpdateEmailTemplateCommand? update = null)
|
|
||||||
{
|
|
||||||
if (update is null)
|
|
||||||
{
|
|
||||||
await _mediator.Send(new ResetEmailTemplateCommand(temp));
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
else if (temp is null)
|
|
||||||
{
|
|
||||||
return BadRequest("No both id and type");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
update.EmailTemplateQuery = temp;
|
|
||||||
await _mediator.Send(update);
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
using EnvelopeGenerator.API.Extensions;
|
|
||||||
using EnvelopeGenerator.Application.Envelopes.Commands;
|
|
||||||
using EnvelopeGenerator.Application.Envelopes.Queries;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Dieser Controller stellt Endpunkte für die Verwaltung von Umschlägen bereit.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Die API ermöglicht das Abrufen und Verwalten von Umschlägen basierend auf Benutzerinformationen und Statusfiltern.
|
|
||||||
///
|
|
||||||
/// Mögliche Antworten:
|
|
||||||
/// - 200 OK: Die Anfrage war erfolgreich, und die angeforderten Daten werden zurückgegeben.
|
|
||||||
/// - 400 Bad Request: Die Anfrage war fehlerhaft oder unvollständig.
|
|
||||||
/// - 401 Unauthorized: Der Benutzer ist nicht authentifiziert.
|
|
||||||
/// - 403 Forbidden: Der Benutzer hat keine Berechtigung, auf die Ressource zuzugreifen.
|
|
||||||
/// - 404 Not Found: Die angeforderte Ressource wurde nicht gefunden.
|
|
||||||
/// - 500 Internal Server Error: Ein unerwarteter Fehler ist aufgetreten.
|
|
||||||
/// </remarks>
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class EnvelopeController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly ILogger<EnvelopeController> _logger;
|
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Erstellt eine neue Instanz des EnvelopeControllers.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger">Der Logger, der für das Protokollieren von Informationen verwendet wird.</param>
|
|
||||||
/// <param name="mediator"></param>
|
|
||||||
public EnvelopeController(ILogger<EnvelopeController> logger, IMediator mediator)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_mediator = mediator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ruft eine Liste von Umschlägen basierend auf dem Benutzer und den angegebenen Statusfiltern ab.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelope"></param>
|
|
||||||
/// <returns>Eine IActionResult-Instanz, die die abgerufenen Umschläge oder einen Fehlerstatus enthält.</returns>
|
|
||||||
/// <response code="200">Die Anfrage war erfolgreich, und die Umschläge werden zurückgegeben.</response>
|
|
||||||
/// <response code="400">Die Anfrage war fehlerhaft oder unvollständig.</response>
|
|
||||||
/// <response code="401">Der Benutzer ist nicht authentifiziert.</response>
|
|
||||||
/// <response code="403">Der Benutzer hat keine Berechtigung, auf die Ressource zuzugreifen.</response>
|
|
||||||
/// <response code="500">Ein unerwarteter Fehler ist aufgetreten.</response>
|
|
||||||
[Authorize]
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetAsync([FromQuery] ReadEnvelopeQuery envelope)
|
|
||||||
{
|
|
||||||
var result = await _mediator.Send(envelope.Authorize(User.GetId()));
|
|
||||||
return result.Any() ? Ok(result) : NotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ruft das Ergebnis eines Dokuments basierend auf der ID ab.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="query"></param>
|
|
||||||
/// <param name="view">Gibt an, ob das Dokument inline angezeigt werden soll (true) oder als Download bereitgestellt wird (false).</param>
|
|
||||||
/// <returns>Eine IActionResult-Instanz, die das Dokument oder einen Fehlerstatus enthält.</returns>
|
|
||||||
/// <response code="200">Das Dokument wurde erfolgreich abgerufen.</response>
|
|
||||||
/// <response code="404">Das Dokument wurde nicht gefunden oder ist nicht verfügbar.</response>
|
|
||||||
/// <response code="500">Ein unerwarteter Fehler ist aufgetreten.</response>
|
|
||||||
[HttpGet("doc-result")]
|
|
||||||
public async Task<IActionResult> GetDocResultAsync([FromQuery] ReadEnvelopeQuery query, [FromQuery] bool view = false)
|
|
||||||
{
|
|
||||||
var envelopes = await _mediator.Send(query.Authorize(User.GetId()));
|
|
||||||
var envelope = envelopes.FirstOrDefault();
|
|
||||||
|
|
||||||
if (envelope is null)
|
|
||||||
return NotFound("Envelope not available.");
|
|
||||||
if (envelope.DocResult is null)
|
|
||||||
return NotFound("The document has not been fully signed or the result has not yet been released.");
|
|
||||||
|
|
||||||
if (view)
|
|
||||||
{
|
|
||||||
Response.Headers.Append("Content-Disposition", "inline; filename=\"" + envelope.Uuid + ".pdf\"");
|
|
||||||
return File(envelope.DocResult, "application/pdf");
|
|
||||||
}
|
|
||||||
|
|
||||||
return File(envelope.DocResult, "application/pdf", $"{envelope.Uuid}.pdf");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="command"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[NonAction]
|
|
||||||
[Authorize]
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<IActionResult> CreateAsync([FromBody] CreateEnvelopeCommand command)
|
|
||||||
{
|
|
||||||
var res = await _mediator.Send(command.Authorize(User.GetId()));
|
|
||||||
|
|
||||||
if (res is null)
|
|
||||||
{
|
|
||||||
_logger.LogError("Failed to create envelope. Envelope details: {EnvelopeDetails}", JsonConvert.SerializeObject(command));
|
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return Ok(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Commands;
|
|
||||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
|
||||||
using EnvelopeGenerator.Application.Envelopes.Queries;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using EnvelopeGenerator.API.Models;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Data.SqlClient;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using System.Data;
|
|
||||||
using EnvelopeGenerator.Application.Common.SQL;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
using EnvelopeGenerator.API.Extensions;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Controller für die Verwaltung von Umschlagempfängern.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Dieser Controller bietet Endpunkte für das Abrufen und Verwalten von Umschlagempfängerdaten.
|
|
||||||
/// </remarks>
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
[Authorize]
|
|
||||||
[ApiController]
|
|
||||||
public class EnvelopeReceiverController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly ILogger<EnvelopeReceiverController> _logger;
|
|
||||||
private readonly IMediator _mediator;
|
|
||||||
private readonly IMapper _mapper;
|
|
||||||
private readonly IEnvelopeExecutor _envelopeExecutor;
|
|
||||||
private readonly IEnvelopeReceiverExecutor _erExecutor;
|
|
||||||
private readonly IDocumentExecutor _documentExecutor;
|
|
||||||
private readonly string _cnnStr;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Konstruktor für den EnvelopeReceiverController.
|
|
||||||
/// </summary>
|
|
||||||
public EnvelopeReceiverController(ILogger<EnvelopeReceiverController> logger, IMediator mediator, IMapper mapper, IEnvelopeExecutor envelopeExecutor, IEnvelopeReceiverExecutor erExecutor, IDocumentExecutor documentExecutor, IOptions<ConnectionString> csOpt)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_mediator = mediator;
|
|
||||||
_mapper = mapper;
|
|
||||||
_envelopeExecutor = envelopeExecutor;
|
|
||||||
_erExecutor = erExecutor;
|
|
||||||
_documentExecutor = documentExecutor;
|
|
||||||
_cnnStr = csOpt.Value.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ruft eine Liste von Umschlagempfängern basierend auf den angegebenen Abfrageparametern ab.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiver">Die Abfrageparameter für die Filterung von Umschlagempfängern.</param>
|
|
||||||
/// <returns>Eine HTTP-Antwort mit der Liste der gefundenen Umschlagempfänger oder einem Fehlerstatus.</returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// Dieser Endpunkt ermöglicht es, Umschlagempfänger basierend auf dem Benutzernamen und optionalen Statusfiltern abzurufen.
|
|
||||||
/// Wenn der Benutzername nicht ermittelt werden kann, wird ein Serverfehler zurückgegeben.
|
|
||||||
/// </remarks>
|
|
||||||
/// <response code="200">Die Liste der Umschlagempfänger wurde erfolgreich abgerufen.</response>
|
|
||||||
/// <response code="401">Wenn kein autorisierter Token vorhanden ist</response>
|
|
||||||
/// <response code="500">Ein unerwarteter Fehler ist aufgetreten.</response>
|
|
||||||
[Authorize]
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetEnvelopeReceiver([FromQuery] ReadEnvelopeReceiverQuery envelopeReceiver)
|
|
||||||
{
|
|
||||||
envelopeReceiver = envelopeReceiver with { Username = User.GetUsername() };
|
|
||||||
|
|
||||||
var result = await _mediator.Send(envelopeReceiver);
|
|
||||||
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ruft den Namen des zuletzt verwendeten Empfängers basierend auf der angegebenen E-Mail-Adresse ab.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="receiver">Abfrage, bei der nur eine der Angaben ID, Signatur oder E-Mail-Adresse des Empfängers eingegeben werden muss.</param>
|
|
||||||
/// <returns>Eine HTTP-Antwort mit dem Namen des Empfängers oder einem Fehlerstatus.</returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// Dieser Endpunkt ermöglicht es, den Namen des zuletzt verwendeten Empfängers basierend auf der E-Mail-Adresse abzurufen.
|
|
||||||
/// </remarks>
|
|
||||||
/// <response code="200">Der Name des Empfängers wurde erfolgreich abgerufen.</response>
|
|
||||||
/// <response code="401">Wenn kein autorisierter Token vorhanden ist</response>
|
|
||||||
/// <response code="404">Kein Empfänger gefunden.</response>
|
|
||||||
/// <response code="500">Ein unerwarteter Fehler ist aufgetreten.</response>
|
|
||||||
[Authorize]
|
|
||||||
[HttpGet("salute")]
|
|
||||||
public async Task<IActionResult> GetReceiverName([FromQuery] ReadReceiverNameQuery receiver)
|
|
||||||
{
|
|
||||||
var name = await _mediator.Send(receiver);
|
|
||||||
return name is null ? NotFound() : Ok(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Datenübertragungsobjekt mit Informationen zu Umschlägen, Empfängern und Unterschriften.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns>HTTP-Antwort</returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// Sample request:
|
|
||||||
///
|
|
||||||
/// POST /api/envelope
|
|
||||||
/// {
|
|
||||||
/// "title": "Vertragsdokument",
|
|
||||||
/// "message": "Bitte unterschreiben Sie dieses Dokument.",
|
|
||||||
/// "document": {
|
|
||||||
/// "dataAsBase64": "dGVzdC1iYXNlNjQtZGF0YQ=="
|
|
||||||
/// },
|
|
||||||
/// "receivers": [
|
|
||||||
/// {
|
|
||||||
/// "emailAddress": "example@example.com",
|
|
||||||
/// "signatures": [
|
|
||||||
/// {
|
|
||||||
/// "x": 100,
|
|
||||||
/// "y": 200,
|
|
||||||
/// "page": 1
|
|
||||||
/// }
|
|
||||||
/// ],
|
|
||||||
/// "name": "Max Mustermann",
|
|
||||||
/// "phoneNumber": "+49123456789"
|
|
||||||
/// }
|
|
||||||
/// ],
|
|
||||||
/// "tfaEnabled": false
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// </remarks>
|
|
||||||
/// <response code="202">Envelope-Erstellung und Sendeprozessbefehl erfolgreich</response>
|
|
||||||
/// <response code="400">Wenn ein Fehler im HTTP-Body auftritt</response>
|
|
||||||
/// <response code="401">Wenn kein autorisierter Token vorhanden ist</response>
|
|
||||||
/// <response code="500">Es handelt sich um einen unerwarteten Fehler. Die Protokolle sollten überprüft werden.</response>
|
|
||||||
[Authorize]
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<IActionResult> CreateAsync([FromBody] CreateEnvelopeReceiverCommand request, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
#region Create Envelope
|
|
||||||
var envelope = await _envelopeExecutor.CreateEnvelopeAsync(User.GetId(), request.Title, request.Message, request.TFAEnabled, cancel);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Add receivers
|
|
||||||
List<EnvelopeReceiver> sentReceivers = new();
|
|
||||||
List<ReceiverGetOrCreateCommand> unsentReceivers = new();
|
|
||||||
|
|
||||||
foreach (var receiver in request.Receivers)
|
|
||||||
{
|
|
||||||
var envelopeReceiver = await _erExecutor.AddEnvelopeReceiverAsync(envelope.Uuid, receiver.EmailAddress, receiver.Salution, receiver.PhoneNumber, cancel);
|
|
||||||
|
|
||||||
if (envelopeReceiver is null)
|
|
||||||
unsentReceivers.Add(receiver);
|
|
||||||
else
|
|
||||||
sentReceivers.Add(envelopeReceiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
var res = _mapper.Map<CreateEnvelopeReceiverResponse>(envelope);
|
|
||||||
res.UnsentReceivers = unsentReceivers;
|
|
||||||
res.SentReceiver = _mapper.Map<List<ReceiverDto>>(sentReceivers.Select(er => er.Receiver));
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Add document
|
|
||||||
var document = await _documentExecutor.CreateDocumentAsync(request.Document.DataAsBase64, envelope.Uuid, cancel);
|
|
||||||
|
|
||||||
if (document is null)
|
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError, "Document creation is failed.");
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Add document element
|
|
||||||
// @DOC_ID, @RECEIVER_ID, @POSITION_X, @POSITION_Y, @PAGE
|
|
||||||
string sql = @"
|
|
||||||
DECLARE @OUT_SUCCESS bit;
|
|
||||||
|
|
||||||
EXEC [dbo].[PRSIG_API_ADD_DOC_RECEIVER_ELEM]
|
|
||||||
{0},
|
|
||||||
{1},
|
|
||||||
{2},
|
|
||||||
{3},
|
|
||||||
{4},
|
|
||||||
@OUT_SUCCESS OUTPUT;
|
|
||||||
|
|
||||||
SELECT @OUT_SUCCESS as [@OUT_SUCCESS];";
|
|
||||||
|
|
||||||
foreach (var rcv in res.SentReceiver)
|
|
||||||
foreach (var sign in request.Receivers.Where(r => r.EmailAddress == rcv.EmailAddress).FirstOrDefault()?.Signatures ?? Enumerable.Empty<Application.EnvelopeReceivers.Commands.Signature>())
|
|
||||||
{
|
|
||||||
using SqlConnection conn = new(_cnnStr);
|
|
||||||
conn.Open();
|
|
||||||
|
|
||||||
var formattedSQL = string.Format(sql, document.Id.ToSqlParam(), rcv.Id.ToSqlParam(), sign.X.ToSqlParam(), sign.Y.ToSqlParam(), sign.Page.ToSqlParam());
|
|
||||||
|
|
||||||
using SqlCommand cmd = new(formattedSQL, conn);
|
|
||||||
cmd.CommandType = CommandType.Text;
|
|
||||||
|
|
||||||
using SqlDataReader reader = cmd.ExecuteReader();
|
|
||||||
if (reader.Read())
|
|
||||||
{
|
|
||||||
bool outSuccess = reader.GetBoolean(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Create history
|
|
||||||
// ENV_UID, STATUS_ID, USER_ID,
|
|
||||||
string sql_hist = @"
|
|
||||||
USE [DD_ECM]
|
|
||||||
|
|
||||||
DECLARE @OUT_SUCCESS bit;
|
|
||||||
|
|
||||||
EXEC [dbo].[PRSIG_API_ADD_HISTORY_STATE]
|
|
||||||
{0},
|
|
||||||
{1},
|
|
||||||
{2},
|
|
||||||
@OUT_SUCCESS OUTPUT;
|
|
||||||
|
|
||||||
SELECT @OUT_SUCCESS as [@OUT_SUCCESS];";
|
|
||||||
|
|
||||||
using (SqlConnection conn = new(_cnnStr))
|
|
||||||
{
|
|
||||||
conn.Open();
|
|
||||||
var formattedSQL_hist = string.Format(sql_hist, envelope.Uuid.ToSqlParam(), 1003.ToSqlParam(), User.GetId().ToSqlParam());
|
|
||||||
using SqlCommand cmd = new(formattedSQL_hist, conn);
|
|
||||||
cmd.CommandType = CommandType.Text;
|
|
||||||
|
|
||||||
using SqlDataReader reader = cmd.ExecuteReader();
|
|
||||||
if (reader.Read())
|
|
||||||
{
|
|
||||||
bool outSuccess = reader.GetBoolean(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
return Ok(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="input"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static bool IsBase64String(string input)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(input))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Convert.FromBase64String(input);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (FormatException)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using EnvelopeGenerator.Application.EnvelopeTypes.Queries;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
[ApiController]
|
|
||||||
public class EnvelopeTypeController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly ILogger<EnvelopeTypeController> _logger;
|
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger"></param>
|
|
||||||
/// <param name="mediator"></param>
|
|
||||||
public EnvelopeTypeController(ILogger<EnvelopeTypeController> logger, IMediator mediator)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_mediator = mediator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetAllAsync()
|
|
||||||
{
|
|
||||||
var result = await _mediator.Send(new ReadEnvelopeTypesQuery());
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
using EnvelopeGenerator.Application.Histories.Queries;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Dieser Controller stellt Endpunkte für den Zugriff auf die Umschlaghistorie bereit.
|
|
||||||
/// </summary>
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class HistoryController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IMemoryCache _memoryCache;
|
|
||||||
|
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Konstruktor für den HistoryController.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="memoryCache"></param>
|
|
||||||
/// <param name="mediator"></param>
|
|
||||||
public HistoryController(IMemoryCache memoryCache, IMediator mediator)
|
|
||||||
{
|
|
||||||
_memoryCache = memoryCache;
|
|
||||||
_mediator = mediator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gibt alle möglichen Verweise auf alle möglichen Include in einem Verlaufsdatensatz zurück. (z. B. DocumentSigned bezieht sich auf Receiver.)
|
|
||||||
/// Dies wird hinzugefügt, damit Client-Anwendungen sich selbst auf dem neuesten Stand halten können.
|
|
||||||
/// 1 - Sender:
|
|
||||||
/// Historische Datensätze über den Include der Empfänger. Diese haben Statuscodes, die mit 1* beginnen.
|
|
||||||
/// 2 - Receiver:
|
|
||||||
/// Historische Datensätze, die sich auf den Include des Absenders beziehen. Sie haben Statuscodes, die mit 2* beginnen.
|
|
||||||
/// 3 - System:
|
|
||||||
/// Historische Datensätze, die sich auf den allgemeinen Zustand des Umschlags beziehen. Diese haben Statuscodes, die mit 3* beginnen.
|
|
||||||
/// 4 - Unknown:
|
|
||||||
/// Ein unbekannter Datensatz weist auf einen möglichen Mangel oder eine Unstimmigkeit im Aktualisierungsprozess der Anwendung hin.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <response code="200"></response>
|
|
||||||
[HttpGet("related")]
|
|
||||||
[Authorize]
|
|
||||||
public IActionResult GetReferenceTypes(ReferenceType? referenceType = null)
|
|
||||||
{
|
|
||||||
return referenceType is null
|
|
||||||
? Ok(_memoryCache.GetEnumAsDictionary<ReferenceType>("gen.api", ReferenceType.Unknown))
|
|
||||||
: Ok(referenceType.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gibt alle möglichen Include in einem Verlaufsdatensatz zurück.
|
|
||||||
/// Dies wird hinzugefügt, damit Client-Anwendungen sich selbst auf dem neuesten Stand halten können.
|
|
||||||
/// 1003: EnvelopeQueued
|
|
||||||
/// 1006: EnvelopeCompletelySigned
|
|
||||||
/// 1007: EnvelopeReportCreated
|
|
||||||
/// 1008: EnvelopeArchived
|
|
||||||
/// 1009: EnvelopeDeleted
|
|
||||||
/// 10007: EnvelopeRejected
|
|
||||||
/// 10009: EnvelopeWithdrawn
|
|
||||||
/// 2001: AccessCodeRequested
|
|
||||||
/// 2002: AccessCodeCorrect
|
|
||||||
/// 2003: AccessCodeIncorrect
|
|
||||||
/// 2004: DocumentOpened
|
|
||||||
/// 2005: DocumentSigned
|
|
||||||
/// 2006: DocumentForwarded
|
|
||||||
/// 2007: DocumentRejected
|
|
||||||
/// 2008: EnvelopeShared
|
|
||||||
/// 2009: EnvelopeViewed
|
|
||||||
/// 3001: MessageInvitationSent (Wird von Trigger verwendet)
|
|
||||||
/// 3002: MessageAccessCodeSent
|
|
||||||
/// 3003: MessageConfirmationSent
|
|
||||||
/// 3004: MessageDeletionSent
|
|
||||||
/// 3005: MessageCompletionSent
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="status">
|
|
||||||
/// Abfrageparameter, der angibt, auf welche Referenz sich der Include bezieht.
|
|
||||||
/// 1 - Sender: Historische Datensätze, die sich auf den Include des Absenders beziehen. Sie haben Statuscodes, die mit 1* beginnen.
|
|
||||||
/// 2 - Receiver: Historische Datensätze über den Include der Empfänger. Diese haben Statuscodes, die mit 2* beginnen.
|
|
||||||
/// 3 - System: Diese werden durch Datenbank-Trigger aktualisiert und sind in den Tabellen EnvelopeHistory und EmailOut zu finden.Sie arbeiten
|
|
||||||
/// integriert mit der Anwendung EmailProfiler, um E-Mails zu versenden und haben die Codes, die mit 3* beginnen.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>Gibt die HTTP-Antwort zurück.</returns>
|
|
||||||
/// <response code="200"></response>
|
|
||||||
[HttpGet("status")]
|
|
||||||
[Authorize]
|
|
||||||
public IActionResult GetEnvelopeStatus([FromQuery] EnvelopeStatus? status = null)
|
|
||||||
{
|
|
||||||
return status is null
|
|
||||||
? Ok(_memoryCache.GetEnumAsDictionary<EnvelopeStatus>("gen.api", Status.NonHist, Status.RelatedToFormApp))
|
|
||||||
: Ok(status.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ruft die gesamte Umschlaghistorie basierend auf den angegebenen Abfrageparametern ab.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="historyQuery">Die Abfrageparameter, die die Filterkriterien für die Umschlaghistorie definieren.</param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns>Eine Liste von Historieneinträgen, die den angegebenen Kriterien entsprechen, oder nur der letzte Eintrag.</returns>
|
|
||||||
/// <response code="200">Die Anfrage war erfolgreich, und die Umschlaghistorie wird zurückgegeben.</response>
|
|
||||||
/// <response code="400">Die Anfrage war ungültig oder unvollständig.</response>
|
|
||||||
/// <response code="401">Der Benutzer ist nicht authentifiziert.</response>
|
|
||||||
/// <response code="403">Der Benutzer hat keine Berechtigung, auf die Ressource zuzugreifen.</response>
|
|
||||||
/// <response code="500">Ein unerwarteter Fehler ist aufgetreten.</response>
|
|
||||||
[HttpGet]
|
|
||||||
[Authorize]
|
|
||||||
public async Task<IActionResult> GetAllAsync([FromQuery] ReadHistoryQuery historyQuery, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
var history = await _mediator.Send(historyQuery, cancel).ThrowIfEmpty(Exceptions.NotFound);
|
|
||||||
return Ok((historyQuery.OnlyLast) ? history.MaxBy(h => h.AddedWhen) : history);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
using DigitalData.Core.API;
|
|
||||||
using EnvelopeGenerator.Application.Resources;
|
|
||||||
using Microsoft.AspNetCore.Localization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using EnvelopeGenerator.Application.Resources;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Controller für die Verwaltung der Lokalisierung und Spracheinstellungen.
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
[ApiController]
|
|
||||||
public class LocalizationController : ControllerBase
|
|
||||||
{
|
|
||||||
private static readonly Guid L_KEY = Guid.NewGuid();
|
|
||||||
|
|
||||||
private readonly ILogger<LocalizationController> _logger;
|
|
||||||
private readonly IStringLocalizer<Resource> _mLocalizer;
|
|
||||||
private readonly IStringLocalizer<Resource> _localizer;
|
|
||||||
private readonly IMemoryCache _cache;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Konstruktor für den <see cref="LocalizationController"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger">Logger für die Protokollierung.</param>
|
|
||||||
/// <param name="localizer">Lokalisierungsdienst für Ressourcen.</param>
|
|
||||||
/// <param name="memoryCache">Speicher-Cache für die Zwischenspeicherung von Daten.</param>
|
|
||||||
/// <param name="_modelLocalizer">Lokalisierungsdienst für Modelle.</param>
|
|
||||||
public LocalizationController(
|
|
||||||
ILogger<LocalizationController> logger,
|
|
||||||
IStringLocalizer<Resource> localizer,
|
|
||||||
IMemoryCache memoryCache,
|
|
||||||
IStringLocalizer<Resource> _modelLocalizer)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_localizer = localizer;
|
|
||||||
_cache = memoryCache;
|
|
||||||
_mLocalizer = _modelLocalizer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ruft alle lokalisierten Daten ab.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Eine Liste aller lokalisierten Daten.</returns>
|
|
||||||
[HttpGet]
|
|
||||||
public IActionResult GetAll() => Ok(_cache.GetOrCreate(Language ?? string.Empty + L_KEY, _ => _mLocalizer.ToDictionary()));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ruft die aktuelle Sprache ab.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Die aktuelle Sprache oder ein NotFound-Ergebnis, wenn keine Sprache gesetzt ist.</returns>
|
|
||||||
[HttpGet("lang")]
|
|
||||||
public IActionResult GetLanguage() => Language is null ? NotFound() : Ok(Language);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Setzt die Sprache.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="language">Die zu setzende Sprache.</param>
|
|
||||||
/// <returns>Ein Ok-Ergebnis, wenn die Sprache erfolgreich gesetzt wurde, oder ein BadRequest-Ergebnis, wenn die Eingabe ungültig ist.</returns>
|
|
||||||
[HttpPost("lang")]
|
|
||||||
public IActionResult SetLanguage([FromQuery] string language)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(language))
|
|
||||||
return BadRequest();
|
|
||||||
|
|
||||||
Language = language;
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Löscht die aktuelle Sprache.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Ein Ok-Ergebnis, wenn die Sprache erfolgreich gelöscht wurde.</returns>
|
|
||||||
[HttpDelete("lang")]
|
|
||||||
public IActionResult DeleteLanguage()
|
|
||||||
{
|
|
||||||
Language = null;
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Eigenschaft für die Verwaltung der aktuellen Sprache über Cookies.
|
|
||||||
/// </summary>
|
|
||||||
private string? Language
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var cookieValue = Request.Cookies[CookieRequestCultureProvider.DefaultCookieName];
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(cookieValue))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var culture = CookieRequestCultureProvider.ParseCookieValue(cookieValue)?.Cultures[0];
|
|
||||||
return culture?.Value ?? null;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value is null)
|
|
||||||
Response.Cookies.Delete(CookieRequestCultureProvider.DefaultCookieName);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var cookieOptions = new CookieOptions()
|
|
||||||
{
|
|
||||||
Expires = DateTimeOffset.UtcNow.AddYears(1),
|
|
||||||
Secure = false,
|
|
||||||
SameSite = SameSiteMode.Strict,
|
|
||||||
HttpOnly = true
|
|
||||||
};
|
|
||||||
|
|
||||||
Response.Cookies.Append(
|
|
||||||
CookieRequestCultureProvider.DefaultCookieName,
|
|
||||||
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(value)),
|
|
||||||
cookieOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.DTO;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.API.Extensions;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Manages read-only envelope sharing flows.
|
|
||||||
/// </summary>
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
[ApiController]
|
|
||||||
public class ReadOnlyController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly ILogger<ReadOnlyController> _logger;
|
|
||||||
private readonly IEnvelopeReceiverReadOnlyService _readOnlyService;
|
|
||||||
private readonly IEnvelopeMailService _mailService;
|
|
||||||
private readonly IEnvelopeHistoryService _historyService;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="ReadOnlyController"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public ReadOnlyController(ILogger<ReadOnlyController> logger, IEnvelopeReceiverReadOnlyService readOnlyService, IEnvelopeMailService mailService, IEnvelopeHistoryService historyService)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_readOnlyService = readOnlyService;
|
|
||||||
_mailService = mailService;
|
|
||||||
_historyService = historyService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new read-only receiver for the current envelope.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="createDto">Creation payload.</param>
|
|
||||||
[HttpPost]
|
|
||||||
[Authorize(Roles = Role.Receiver.FullyAuth)]
|
|
||||||
public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto)
|
|
||||||
{
|
|
||||||
var authReceiverMail = User.GetReceiverMailOfReceiver();
|
|
||||||
if (authReceiverMail is null)
|
|
||||||
{
|
|
||||||
_logger.LogError("EmailAddress claim is not found in envelope-receiver-read-only creation process. Create DTO is:\n {dto}", JsonConvert.SerializeObject(createDto));
|
|
||||||
return Unauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
var envelopeId = User.GetEnvelopeIdOfReceiver();
|
|
||||||
|
|
||||||
createDto.AddedWho = authReceiverMail;
|
|
||||||
createDto.EnvelopeId = envelopeId;
|
|
||||||
|
|
||||||
var creationRes = await _readOnlyService.CreateAsync(createDto: createDto);
|
|
||||||
|
|
||||||
if (creationRes.IsFailed)
|
|
||||||
{
|
|
||||||
_logger.LogNotice(creationRes);
|
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
|
||||||
}
|
|
||||||
|
|
||||||
var readRes = await _readOnlyService.ReadByIdAsync(creationRes.Data.Id);
|
|
||||||
if (readRes.IsFailed)
|
|
||||||
{
|
|
||||||
_logger.LogNotice(creationRes);
|
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
|
||||||
}
|
|
||||||
|
|
||||||
var newReadOnly = readRes.Data;
|
|
||||||
|
|
||||||
return await _mailService.SendAsync(newReadOnly).ThenAsync<int, IActionResult>(SuccessAsync: async _ =>
|
|
||||||
{
|
|
||||||
var histRes = await _historyService.RecordAsync((int)createDto.EnvelopeId, createDto.AddedWho, EnvelopeStatus.EnvelopeShared);
|
|
||||||
if (histRes.IsFailed)
|
|
||||||
{
|
|
||||||
_logger.LogError("Although the envelope was sent as read-only, the EnvelopeShared history could not be saved. Create DTO:\n{createDto}", JsonConvert.SerializeObject(createDto));
|
|
||||||
_logger.LogNotice(histRes.Notices);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok();
|
|
||||||
},
|
|
||||||
|
|
||||||
Fail: (msg, ntc) =>
|
|
||||||
{
|
|
||||||
_logger.LogNotice(ntc);
|
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using EnvelopeGenerator.Application.Receivers.Queries;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Controller für die Verwaltung von Empfängern.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Dieser Controller bietet Endpunkte für das Abrufen von Empfängern basierend auf E-Mail-Adresse oder Signatur.
|
|
||||||
/// </remarks>
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
public class ReceiverController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialisiert eine neue Instanz des <see cref="ReceiverController"/>-Controllers.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mediator">Mediator für Anfragen.</param>
|
|
||||||
public ReceiverController(IMediator mediator)
|
|
||||||
{
|
|
||||||
_mediator = mediator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ruft eine Liste von Empfängern ab, basierend auf den angegebenen Abfrageparametern.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="receiver">Die Abfrageparameter, einschließlich E-Mail-Adresse und Signatur.</param>
|
|
||||||
/// <returns>Eine Liste von Empfängern oder ein Fehlerstatus.</returns>
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> Get([FromQuery] ReadReceiverQuery receiver)
|
|
||||||
{
|
|
||||||
if (!receiver.HasAnyCriteria)
|
|
||||||
{
|
|
||||||
var all = await _mediator.Send(new ReadReceiverQuery());
|
|
||||||
return Ok(all);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = await _mediator.Send(receiver);
|
|
||||||
return result is null ? NotFound() : Ok(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.DTO;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
using EnvelopeGenerator.Application.Resources;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.API.Models;
|
|
||||||
using Ganss.Xss;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Exposes endpoints for registering and managing two-factor authentication for envelope receivers.
|
|
||||||
/// </summary>
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/tfa")]
|
|
||||||
public class TfaRegistrationController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly ILogger<TfaRegistrationController> _logger;
|
|
||||||
private readonly IEnvelopeReceiverService _envelopeReceiverService;
|
|
||||||
private readonly IAuthenticator _authenticator;
|
|
||||||
private readonly IReceiverService _receiverService;
|
|
||||||
private readonly TFARegParams _parameters;
|
|
||||||
private readonly IStringLocalizer<Resource> _localizer;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="TfaRegistrationController"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public TfaRegistrationController(
|
|
||||||
ILogger<TfaRegistrationController> logger,
|
|
||||||
IEnvelopeReceiverService envelopeReceiverService,
|
|
||||||
IAuthenticator authenticator,
|
|
||||||
IReceiverService receiverService,
|
|
||||||
IOptions<TFARegParams> tfaRegParamsOptions,
|
|
||||||
IStringLocalizer<Resource> localizer)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_envelopeReceiverService = envelopeReceiverService;
|
|
||||||
_authenticator = authenticator;
|
|
||||||
_receiverService = receiverService;
|
|
||||||
_parameters = tfaRegParamsOptions.Value;
|
|
||||||
_localizer = localizer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates registration metadata (QR code and deadline) for a receiver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiverId">Encoded envelope receiver id.</param>
|
|
||||||
[Authorize]
|
|
||||||
[HttpGet("{envelopeReceiverId}")]
|
|
||||||
public async Task<IActionResult> RegisterAsync(string envelopeReceiverId)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var (uuid, signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
|
|
||||||
|
|
||||||
if (uuid is null || signature is null)
|
|
||||||
{
|
|
||||||
_logger.LogEnvelopeError(uuid: uuid, signature: signature, message: _localizer.WrongEnvelopeReceiverId());
|
|
||||||
return Unauthorized(new { message = _localizer.WrongEnvelopeReceiverId() });
|
|
||||||
}
|
|
||||||
|
|
||||||
var secretResult = await _envelopeReceiverService.ReadWithSecretByUuidSignatureAsync(uuid: uuid, signature: signature);
|
|
||||||
if (secretResult.IsFailed)
|
|
||||||
{
|
|
||||||
_logger.LogNotice(secretResult.Notices);
|
|
||||||
return NotFound(new { message = _localizer.WrongEnvelopeReceiverId() });
|
|
||||||
}
|
|
||||||
|
|
||||||
var envelopeReceiver = secretResult.Data;
|
|
||||||
|
|
||||||
if (!envelopeReceiver.Envelope!.TFAEnabled)
|
|
||||||
return Unauthorized(new { message = _localizer.WrongAccessCode() });
|
|
||||||
|
|
||||||
var receiver = envelopeReceiver.Receiver;
|
|
||||||
receiver!.TotpSecretkey = _authenticator.GenerateTotpSecretKey();
|
|
||||||
await _receiverService.UpdateAsync(receiver);
|
|
||||||
var totpQr64 = _authenticator.GenerateTotpQrCode(userEmail: receiver.EmailAddress, secretKey: receiver.TotpSecretkey).ToBase64String();
|
|
||||||
|
|
||||||
if (receiver.TfaRegDeadline is null)
|
|
||||||
{
|
|
||||||
receiver.TfaRegDeadline = _parameters.Deadline;
|
|
||||||
await _receiverService.UpdateAsync(receiver);
|
|
||||||
}
|
|
||||||
else if (receiver.TfaRegDeadline <= DateTime.Now)
|
|
||||||
{
|
|
||||||
return StatusCode(StatusCodes.Status410Gone, new { message = _localizer.WrongAccessCode() });
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(new
|
|
||||||
{
|
|
||||||
envelopeReceiver.EnvelopeId,
|
|
||||||
envelopeReceiver.Envelope!.Uuid,
|
|
||||||
envelopeReceiver.Receiver!.Signature,
|
|
||||||
receiver.TfaRegDeadline,
|
|
||||||
TotpQR64 = totpQr64
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception: ex, message: _localizer.WrongEnvelopeReceiverId());
|
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError, new { message = _localizer.UnexpectedError() });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Logs out the envelope receiver from cookie authentication.
|
|
||||||
/// </summary>
|
|
||||||
[Authorize(Roles = Role.FullyAuth)]
|
|
||||||
[HttpPost("auth/logout")]
|
|
||||||
public async Task<IActionResult> LogOutAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "{message}", ex.Message);
|
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError, new { message = _localizer.UnexpectedError() });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
using EnvelopeGenerator.API.Models;
|
|
||||||
using Microsoft.OpenApi.Any;
|
|
||||||
using Microsoft.OpenApi.Models;
|
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Documentation;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public sealed class AuthProxyDocumentFilter : IDocumentFilter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="swaggerDoc"></param>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
|
||||||
{
|
|
||||||
const string path = "/api/auth";
|
|
||||||
|
|
||||||
var loginSchema = context.SchemaGenerator.GenerateSchema(typeof(Login), context.SchemaRepository);
|
|
||||||
var loginExample = new OpenApiObject
|
|
||||||
{
|
|
||||||
["password"] = new OpenApiString(""),
|
|
||||||
["username"] = new OpenApiString("")
|
|
||||||
};
|
|
||||||
|
|
||||||
var operation = new OpenApiOperation
|
|
||||||
{
|
|
||||||
Summary = "Proxy login (auth-hub)",
|
|
||||||
Description = "Proxies the request to the auth service. Add query parameter `cookie=true|false`.",
|
|
||||||
Tags = [new() { Name = "Auth" }],
|
|
||||||
Parameters =
|
|
||||||
{
|
|
||||||
new OpenApiParameter
|
|
||||||
{
|
|
||||||
Name = "cookie",
|
|
||||||
In = ParameterLocation.Query,
|
|
||||||
Required = false,
|
|
||||||
Schema = new OpenApiSchema { Type = "boolean", Default = new OpenApiBoolean(true) },
|
|
||||||
Example = new OpenApiBoolean(true),
|
|
||||||
Description = "If true, auth service sets the auth cookie."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
RequestBody = new OpenApiRequestBody
|
|
||||||
{
|
|
||||||
Required = true,
|
|
||||||
Content =
|
|
||||||
{
|
|
||||||
["application/json"] = new OpenApiMediaType { Schema = loginSchema, Example = loginExample },
|
|
||||||
["multipart/form-data"] = new OpenApiMediaType { Schema = loginSchema, Example = loginExample }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Responses =
|
|
||||||
{
|
|
||||||
["200"] = new OpenApiResponse { Description = "OK (proxied response)" },
|
|
||||||
["401"] = new OpenApiResponse { Description = "Unauthorized" }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
swaggerDoc.Paths[path] = new OpenApiPathItem
|
|
||||||
{
|
|
||||||
Operations =
|
|
||||||
{
|
|
||||||
[OperationType.Post] = operation
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides custom claim types for envelope-related information.
|
|
||||||
/// </summary>
|
|
||||||
public static class EnvelopeClaimTypes
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Claim type for the title of an envelope.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly string Title = $"Envelope{nameof(Title)}";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Claim type for the ID of an envelope.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly string Id = $"Envelope{nameof(Id)}";
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFrameworks>net9.0</TargetFrameworks>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
|
||||||
<PackageId>EnvelopeGenerator.GeneratorAPI</PackageId>
|
|
||||||
<Title></Title>
|
|
||||||
<Authors>Digital Data GmbH</Authors>
|
|
||||||
<Company>Digital Data GmbH</Company>
|
|
||||||
<Product>EnvelopeGenerator.GeneratorAPI</Product>
|
|
||||||
<Version>1.2.3</Version>
|
|
||||||
<FileVersion>1.2.3</FileVersion>
|
|
||||||
<AssemblyVersion>1.2.3</AssemblyVersion>
|
|
||||||
<PackageOutputPath>Copyright © 2025 Digital Data GmbH. All rights reserved.</PackageOutputPath>
|
|
||||||
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Remove="ClientApp\**" />
|
|
||||||
<Content Remove="ClientApp\**" />
|
|
||||||
<EmbeddedResource Remove="ClientApp\**" />
|
|
||||||
<None Remove="ClientApp\**" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="yarp.json" CopyToOutputDirectory="PreserveNewest" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="AspNetCore.Scalar" Version="1.1.8" />
|
|
||||||
<PackageReference Include="DigitalData.Auth.Client" Version="1.3.7" />
|
|
||||||
<PackageReference Include="DigitalData.Core.API" Version="2.2.1" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.4" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.6" />
|
|
||||||
<PackageReference Include="NLog" Version="5.2.5" />
|
|
||||||
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.0" />
|
|
||||||
<PackageReference Include="Scalar.AspNetCore" Version="2.2.1" />
|
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
|
||||||
<PackageReference Include="DigitalData.EmailProfilerDispatcher.Abstraction" Version="3.2.0" />
|
|
||||||
<PackageReference Include="Yarp.ReverseProxy" Version="2.1.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
|
||||||
<PackageReference Include="System.DirectoryServices" Version="7.0.1" />
|
|
||||||
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="7.0.1" />
|
|
||||||
<PackageReference Include="System.DirectoryServices.Protocols" Version="7.0.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
|
||||||
<PackageReference Include="System.DirectoryServices" Version="8.0.0" />
|
|
||||||
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="8.0.1" />
|
|
||||||
<PackageReference Include="System.DirectoryServices.Protocols" Version="8.0.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
|
||||||
<PackageReference Include="System.DirectoryServices" Version="9.0.4" />
|
|
||||||
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="9.0.4" />
|
|
||||||
<PackageReference Include="System.DirectoryServices.Protocols" Version="9.0.4" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="wwwroot\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
|
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
|
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides helper methods for working with envelope-specific authentication claims.
|
|
||||||
/// </summary>
|
|
||||||
public static class ReceiverClaimExtensions
|
|
||||||
{
|
|
||||||
private static string GetRequiredClaimOfReceiver(this ClaimsPrincipal user, string claimType)
|
|
||||||
{
|
|
||||||
var value = user.FindFirstValue(claimType);
|
|
||||||
if (value is not null)
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var identity = user.Identity;
|
|
||||||
var principalName = identity?.Name ?? "(anonymous)";
|
|
||||||
var authType = identity?.AuthenticationType ?? "(none)";
|
|
||||||
var availableClaims = string.Join(", ", user.Claims.Select(c => $"{c.Type}={c.Value}"));
|
|
||||||
var message = $"Required claim '{claimType}' is missing for user '{principalName}' (auth: {authType}). Available claims: [{availableClaims}].";
|
|
||||||
throw new InvalidOperationException(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the authenticated envelope UUID from the claims.
|
|
||||||
/// </summary>
|
|
||||||
public static string GetEnvelopeUuidOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.NameIdentifier);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the authenticated receiver signature from the claims.
|
|
||||||
/// </summary>
|
|
||||||
public static string GetReceiverSignatureOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.Hash);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the authenticated receiver display name from the claims.
|
|
||||||
/// </summary>
|
|
||||||
public static string GetReceiverNameOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.Name);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the authenticated receiver email address from the claims.
|
|
||||||
/// </summary>
|
|
||||||
public static string GetReceiverMailOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.Email);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the authenticated envelope title from the claims.
|
|
||||||
/// </summary>
|
|
||||||
public static string GetEnvelopeTitleOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(EnvelopeClaimTypes.Title);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the authenticated envelope identifier from the claims.
|
|
||||||
/// </summary>
|
|
||||||
public static int GetEnvelopeIdOfReceiver(this ClaimsPrincipal user)
|
|
||||||
{
|
|
||||||
var envIdStr = user.GetRequiredClaimOfReceiver(EnvelopeClaimTypes.Id);
|
|
||||||
if (!int.TryParse(envIdStr, out var envId))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Claim '{EnvelopeClaimTypes.Id}' is not a valid integer.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return envId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Signs in an envelope receiver using cookie authentication and attaches envelope claims.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context">The current HTTP context.</param>
|
|
||||||
/// <param name="envelopeReceiver">Envelope receiver DTO to extract claims from.</param>
|
|
||||||
/// <param name="receiverRole">Role to attach to the authentication ticket.</param>
|
|
||||||
public static async Task SignInEnvelopeAsync(this HttpContext context, EnvelopeReceiverDto envelopeReceiver, string receiverRole)
|
|
||||||
{
|
|
||||||
var claims = new List<Claim>
|
|
||||||
{
|
|
||||||
new(ClaimTypes.NameIdentifier, envelopeReceiver.Envelope!.Uuid),
|
|
||||||
new(ClaimTypes.Hash, envelopeReceiver.Receiver!.Signature),
|
|
||||||
new(ClaimTypes.Name, envelopeReceiver.Name ?? string.Empty),
|
|
||||||
new(ClaimTypes.Email, envelopeReceiver.Receiver.EmailAddress),
|
|
||||||
new(EnvelopeClaimTypes.Title, envelopeReceiver.Envelope.Title),
|
|
||||||
new(EnvelopeClaimTypes.Id, envelopeReceiver.Envelope.Id.ToString()),
|
|
||||||
new(ClaimTypes.Role, receiverRole)
|
|
||||||
};
|
|
||||||
|
|
||||||
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
|
|
||||||
|
|
||||||
var authProperties = new AuthenticationProperties
|
|
||||||
{
|
|
||||||
AllowRefresh = false,
|
|
||||||
IsPersistent = false
|
|
||||||
};
|
|
||||||
|
|
||||||
await context.SignInAsync(
|
|
||||||
CookieAuthenticationDefaults.AuthenticationScheme,
|
|
||||||
new ClaimsPrincipal(claimsIdentity),
|
|
||||||
authProperties);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
using System.Security.Claims;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Extensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides extension methods for extracting user information from a <see cref="ClaimsPrincipal"/>.
|
|
||||||
/// </summary>
|
|
||||||
public static class SenderClaimExtensions
|
|
||||||
{
|
|
||||||
private static string GetRequiredClaimOfSender(this ClaimsPrincipal user, string claimType)
|
|
||||||
{
|
|
||||||
var value = user.FindFirstValue(claimType);
|
|
||||||
if (value is not null)
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var identity = user.Identity;
|
|
||||||
var principalName = identity?.Name ?? "(anonymous)";
|
|
||||||
var authType = identity?.AuthenticationType ?? "(none)";
|
|
||||||
var availableClaims = string.Join(", ", user.Claims.Select(c => $"{c.Type}={c.Value}"));
|
|
||||||
var message = $"Required claim '{claimType}' is missing for user '{principalName}' (auth: {authType}). Available claims: [{availableClaims}].";
|
|
||||||
throw new InvalidOperationException(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetRequiredClaimOfSender(this ClaimsPrincipal user, params string[] claimTypes)
|
|
||||||
{
|
|
||||||
string? value = null;
|
|
||||||
|
|
||||||
foreach (var claimType in claimTypes)
|
|
||||||
{
|
|
||||||
value = user.FindFirstValue(claimType);
|
|
||||||
if (value is not null)
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var identity = user.Identity;
|
|
||||||
var principalName = identity?.Name ?? "(anonymous)";
|
|
||||||
var authType = identity?.AuthenticationType ?? "(none)";
|
|
||||||
var availableClaims = string.Join(", ", user.Claims.Select(c => $"{c.Type}={c.Value}"));
|
|
||||||
var message = $"Required claim among [{string.Join(", ", claimTypes)}] is missing for user '{principalName}' (auth: {authType}). Available claims: [{availableClaims}].";
|
|
||||||
throw new InvalidOperationException(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the user's ID from the claims. Throws an exception if the ID is missing or invalid.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
||||||
/// <returns>The user's ID as an integer.</returns>
|
|
||||||
/// <exception cref="InvalidOperationException">Thrown if the user ID claim is missing or invalid.</exception>
|
|
||||||
public static int GetId(this ClaimsPrincipal user)
|
|
||||||
{
|
|
||||||
var idValue = user.GetRequiredClaimOfSender(ClaimTypes.NameIdentifier, "sub");
|
|
||||||
|
|
||||||
if (!int.TryParse(idValue, out var result))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("User ID claim is missing or invalid. This may indicate a misconfigured or forged JWT token.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the username from the claims.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
||||||
/// <returns>The username as a string.</returns>
|
|
||||||
public static string GetUsername(this ClaimsPrincipal user)
|
|
||||||
=> user.GetRequiredClaimOfSender(ClaimTypes.Name);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the user's surname (last name) from the claims.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
||||||
/// <returns>The surname as a string.</returns>
|
|
||||||
public static string GetName(this ClaimsPrincipal user)
|
|
||||||
=> user.GetRequiredClaimOfSender(ClaimTypes.Surname);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the user's given name (first name) from the claims.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
||||||
/// <returns>The given name as a string.</returns>
|
|
||||||
public static string GetPrename(this ClaimsPrincipal user)
|
|
||||||
=> user.GetRequiredClaimOfSender(ClaimTypes.GivenName);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the user's email address from the claims.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
||||||
/// <returns>The email address as a string.</returns>
|
|
||||||
public static string GetEmail(this ClaimsPrincipal user)
|
|
||||||
=> user.GetRequiredClaimOfSender(ClaimTypes.Email);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
10
EnvelopeGenerator.API/Jenkinsfile
vendored
10
EnvelopeGenerator.API/Jenkinsfile
vendored
@@ -1,10 +0,0 @@
|
|||||||
pipeline {
|
|
||||||
agent any
|
|
||||||
stages {
|
|
||||||
stage('Build') {
|
|
||||||
steps {
|
|
||||||
sh 'dotnet build'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API.Middleware;
|
|
||||||
|
|
||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Net;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Middleware for handling exceptions globally in the application.
|
|
||||||
/// Captures exceptions thrown during the request pipeline execution,
|
|
||||||
/// logs them, and returns an appropriate HTTP response with a JSON error message.
|
|
||||||
/// </summary>
|
|
||||||
public class ExceptionHandlingMiddleware
|
|
||||||
{
|
|
||||||
private readonly RequestDelegate _next;
|
|
||||||
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="ExceptionHandlingMiddleware"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="next">The next middleware in the request pipeline.</param>
|
|
||||||
/// <param name="logger">The logger instance for logging exceptions.</param>
|
|
||||||
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
|
|
||||||
{
|
|
||||||
_next = next;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invokes the middleware to handle the HTTP request.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context">The HTTP context of the current request.</param>
|
|
||||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
|
||||||
public async Task InvokeAsync(HttpContext context)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _next(context); // Continue down the pipeline
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await HandleExceptionAsync(context, ex, _logger);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles exceptions by logging them and writing an appropriate JSON response.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context">The HTTP context of the current request.</param>
|
|
||||||
/// <param name="exception">The exception that occurred.</param>
|
|
||||||
/// <param name="logger">The logger instance for logging the exception.</param>
|
|
||||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
|
||||||
private static async Task HandleExceptionAsync(HttpContext context, Exception exception, ILogger logger)
|
|
||||||
{
|
|
||||||
context.Response.ContentType = "application/json";
|
|
||||||
|
|
||||||
string message;
|
|
||||||
|
|
||||||
switch (exception)
|
|
||||||
{
|
|
||||||
case BadRequestException badRequestEx:
|
|
||||||
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
|
||||||
message = badRequestEx.Message;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NotFoundException notFoundEx:
|
|
||||||
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
|
||||||
message = notFoundEx.Message;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
logger.LogError(exception, "Unhandled exception occurred.");
|
|
||||||
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
|
||||||
message = "An unexpected error occurred.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
await context.Response.WriteAsync(JsonSerializer.Serialize(new
|
|
||||||
{
|
|
||||||
message
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API.Models;
|
|
||||||
|
|
||||||
public record Auth(string? AccessCode = null, string? SmsCode = null, string? AuthenticatorCode = null, bool UserSelectSMS = default)
|
|
||||||
{
|
|
||||||
public bool HasAccessCode => AccessCode is not null;
|
|
||||||
|
|
||||||
public bool HasSmsCode => SmsCode is not null;
|
|
||||||
|
|
||||||
public bool HasAuthenticatorCode => AuthenticatorCode is not null;
|
|
||||||
|
|
||||||
public bool HasMulti => new[] { HasAccessCode, HasSmsCode, HasAuthenticatorCode }.Count(state => state) > 1;
|
|
||||||
|
|
||||||
public bool HasNone => !(HasAccessCode || HasSmsCode || HasAuthenticatorCode);
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API.Models;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the keys and default values used for authentication token handling
|
|
||||||
/// within the Envelope Generator API.
|
|
||||||
/// </summary>
|
|
||||||
public class AuthTokenKeys
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the name of the cookie used to store the authentication token.
|
|
||||||
/// </summary>
|
|
||||||
public string Cookie { get; init; } = "AuthToken";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the name of the query string parameter used to pass the authentication token.
|
|
||||||
/// </summary>
|
|
||||||
public string QueryString { get; init; } = "AuthToken";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the expected issuer value for the authentication token.
|
|
||||||
/// </summary>
|
|
||||||
public string Issuer { get; init; } = "auth.digitaldata.works";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the expected audience value for the authentication token.
|
|
||||||
/// </summary>
|
|
||||||
public string Audience { get; init; } = "sign-flow.digitaldata.works";
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API.Models;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class ConnectionString
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string Value { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API.Models
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a hyperlink for contact purposes with various HTML attributes.
|
|
||||||
/// </summary>
|
|
||||||
public class ContactLink
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the label of the hyperlink.
|
|
||||||
/// </summary>
|
|
||||||
public string Label { get; init; } = "Contact";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the URL that the hyperlink points to.
|
|
||||||
/// </summary>
|
|
||||||
public string Href { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the target where the hyperlink should open.
|
|
||||||
/// Commonly used values are "_blank", "_self", "_parent", "_top".
|
|
||||||
/// </summary>
|
|
||||||
public string Target { get; set; } = "_blank";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the relationship of the linked URL as space-separated link types.
|
|
||||||
/// Examples include "nofollow", "noopener", "noreferrer".
|
|
||||||
/// </summary>
|
|
||||||
public string Rel { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the filename that should be downloaded when clicking the hyperlink.
|
|
||||||
/// This attribute will only have an effect if the href attribute is set.
|
|
||||||
/// </summary>
|
|
||||||
public string Download { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the language of the linked resource. Useful when linking to
|
|
||||||
/// content in another language.
|
|
||||||
/// </summary>
|
|
||||||
public string HrefLang { get; set; } = "en";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the MIME type of the linked URL. Helps browsers to handle
|
|
||||||
/// the type correctly when the link is clicked.
|
|
||||||
/// </summary>
|
|
||||||
public string Type { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets additional information about the hyperlink, typically viewed
|
|
||||||
/// as a tooltip when the mouse hovers over the link.
|
|
||||||
/// </summary>
|
|
||||||
public string Title { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets an identifier for the hyperlink, unique within the HTML document.
|
|
||||||
/// </summary>
|
|
||||||
public string Id { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Models;
|
|
||||||
|
|
||||||
public class Culture
|
|
||||||
{
|
|
||||||
private string _language = string.Empty;
|
|
||||||
public string Language { get => _language;
|
|
||||||
init {
|
|
||||||
_language = value;
|
|
||||||
Info = new(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public string FIClass { get; init; } = string.Empty;
|
|
||||||
|
|
||||||
public CultureInfo? Info { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API.Models;
|
|
||||||
|
|
||||||
public class Cultures : List<Culture>
|
|
||||||
{
|
|
||||||
public IEnumerable<string> Languages => this.Select(c => c.Language);
|
|
||||||
|
|
||||||
public IEnumerable<string> FIClasses => this.Select(c => c.FIClass);
|
|
||||||
|
|
||||||
public Culture Default => this.First();
|
|
||||||
|
|
||||||
public Culture? this[string? language] => language is null ? null : this.Where(c => c.Language == language).FirstOrDefault();
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API.Models;
|
|
||||||
|
|
||||||
public class CustomImages : Dictionary<string, Image>
|
|
||||||
{
|
|
||||||
public new Image this[string key] => TryGetValue(key, out var img) && img is not null ? img : new();
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API.Models;
|
|
||||||
|
|
||||||
public class ErrorViewModel
|
|
||||||
{
|
|
||||||
public string Title { get; init; } = "404";
|
|
||||||
|
|
||||||
public string Subtitle { get; init; } = "Hmmm...";
|
|
||||||
|
|
||||||
public string Body { get; init; } = "It looks like one of the developers fell asleep";
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API.Models;
|
|
||||||
|
|
||||||
public class Image
|
|
||||||
{
|
|
||||||
public string Src { get; init; } = string.Empty;
|
|
||||||
|
|
||||||
public Dictionary<string, string> Classes { get; init; } = new();
|
|
||||||
|
|
||||||
public string GetClassIn(string page) => Classes.TryGetValue(page, out var cls) && cls is not null ? cls : string.Empty;
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Models;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Repräsentiert ein Login-Modell mit erforderlichem Passwort und optionaler ID und Benutzername.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Password">Das erforderliche Passwort für das Login.</param>
|
|
||||||
/// <param name="UserId">Die optionale ID des Benutzers.</param>
|
|
||||||
/// <param name="Username">Der optionale Benutzername.</param>
|
|
||||||
public record Login([Required] string Password, int? UserId = null, string? Username = null)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API.Models;
|
|
||||||
|
|
||||||
public class MainViewModel
|
|
||||||
{
|
|
||||||
public string? Title { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
using EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
|
||||||
|
|
||||||
public record Annotation : IAnnotation
|
|
||||||
{
|
|
||||||
public required string Name { get; init; }
|
|
||||||
|
|
||||||
#region Bound Annotation
|
|
||||||
[JsonIgnore]
|
|
||||||
public string? HorBoundAnnotName { get; init; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public string? VerBoundAnnotName { get; init; }
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Layout
|
|
||||||
[JsonIgnore]
|
|
||||||
public double? MarginLeft { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public double MarginLeftRatio { get; init; } = 1;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public double? MarginTop { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public double MarginTopRatio { get; init; } = 1;
|
|
||||||
|
|
||||||
public double? Width { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public double WidthRatio { get; init; } = 1;
|
|
||||||
|
|
||||||
public double? Height { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public double HeightRatio { get; init; } = 1;
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Position
|
|
||||||
public double Left => (MarginLeft ?? 0) + (HorBoundAnnot?.HorBoundary ?? 0);
|
|
||||||
|
|
||||||
public double Top => (MarginTop ?? 0) + (VerBoundAnnot?.VerBoundary ?? 0);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Boundary
|
|
||||||
[JsonIgnore]
|
|
||||||
public double HorBoundary => Left + (Width ?? 0);
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public double VerBoundary => Top + (Height ?? 0);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region BoundAnnot
|
|
||||||
[JsonIgnore]
|
|
||||||
public Annotation? HorBoundAnnot { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public Annotation? VerBoundAnnot { get; set; }
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public Color? BackgroundColor { get; init; }
|
|
||||||
|
|
||||||
#region Border
|
|
||||||
public Color? BorderColor { get; init; }
|
|
||||||
|
|
||||||
public string? BorderStyle { get; init; }
|
|
||||||
|
|
||||||
public int? BorderWidth { get; set; }
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
internal Annotation Default
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
// To set null value, annotation must have null (0) value but null must has non-null value
|
|
||||||
if (MarginLeft == null && value.MarginLeft != null)
|
|
||||||
MarginLeft = value.MarginLeft * MarginLeftRatio;
|
|
||||||
|
|
||||||
if (MarginTop == null && value.MarginTop != null)
|
|
||||||
MarginTop = value.MarginTop * MarginTopRatio;
|
|
||||||
|
|
||||||
if (Width == null && value.Width != null)
|
|
||||||
Width = value.Width * WidthRatio;
|
|
||||||
|
|
||||||
if (Height == null && value.Height != null)
|
|
||||||
Height = value.Height * HeightRatio;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
using EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
|
||||||
|
|
||||||
public class AnnotationParams
|
|
||||||
{
|
|
||||||
public AnnotationParams()
|
|
||||||
{
|
|
||||||
_AnnotationJSObjectInitor = new(CreateAnnotationJSObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Background? Background { get; init; }
|
|
||||||
|
|
||||||
#region Annotation
|
|
||||||
[JsonIgnore]
|
|
||||||
public Annotation? DefaultAnnotation { get; init; }
|
|
||||||
|
|
||||||
private readonly List<Annotation> _annots = new List<Annotation>();
|
|
||||||
|
|
||||||
public bool TryGet(string name, out Annotation annotation)
|
|
||||||
{
|
|
||||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
|
||||||
annotation = _annots.FirstOrDefault(a => a.Name == name);
|
|
||||||
#pragma warning restore CS8601 // Possible null reference assignment.
|
|
||||||
return annotation is not null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public required IEnumerable<Annotation> Annotations
|
|
||||||
{
|
|
||||||
get => _annots;
|
|
||||||
init
|
|
||||||
{
|
|
||||||
_annots = value.ToList();
|
|
||||||
|
|
||||||
if (DefaultAnnotation is not null)
|
|
||||||
foreach (var annot in _annots)
|
|
||||||
annot.Default = DefaultAnnotation;
|
|
||||||
|
|
||||||
for (int i = 0; i < _annots.Count; i++)
|
|
||||||
{
|
|
||||||
#region set bound annotations
|
|
||||||
// horizontal
|
|
||||||
if (_annots[i].HorBoundAnnotName is string horBoundAnnotName)
|
|
||||||
if (TryGet(horBoundAnnotName, out var horBoundAnnot))
|
|
||||||
_annots[i].HorBoundAnnot = horBoundAnnot;
|
|
||||||
else
|
|
||||||
throw new InvalidOperationException($"{horBoundAnnotName} added as bound anotation. However, it is not defined.");
|
|
||||||
|
|
||||||
// vertical
|
|
||||||
if (_annots[i].VerBoundAnnotName is string verBoundAnnotName)
|
|
||||||
if (TryGet(verBoundAnnotName, out var verBoundAnnot))
|
|
||||||
_annots[i].VerBoundAnnot = verBoundAnnot;
|
|
||||||
else
|
|
||||||
throw new InvalidOperationException($"{verBoundAnnotName} added as bound anotation. However, it is not defined.");
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region AnnotationJSObject
|
|
||||||
private Dictionary<string, IAnnotation> CreateAnnotationJSObject()
|
|
||||||
{
|
|
||||||
var dict = _annots.ToDictionary(a => a.Name.ToLower(), a => a as IAnnotation);
|
|
||||||
|
|
||||||
if (Background is not null)
|
|
||||||
{
|
|
||||||
Background.Locate(_annots);
|
|
||||||
dict.Add(Background.Name.ToLower(), Background);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dict;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly Lazy<Dictionary<string, IAnnotation>> _AnnotationJSObjectInitor;
|
|
||||||
|
|
||||||
public Dictionary<string, IAnnotation> AnnotationJSObject => _AnnotationJSObjectInitor.Value;
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The Background is an annotation for the PSPDF Kit. However, it has no function.
|
|
||||||
/// It is only the first annotation as a background for other annotations.
|
|
||||||
/// </summary>
|
|
||||||
public record Background : IAnnotation
|
|
||||||
{
|
|
||||||
[JsonIgnore]
|
|
||||||
public double Margin { get; init; }
|
|
||||||
|
|
||||||
public string Name { get; } = "Background";
|
|
||||||
|
|
||||||
public double? Width { get; set; }
|
|
||||||
|
|
||||||
public double? Height { get; set; }
|
|
||||||
|
|
||||||
public double Left { get; set; }
|
|
||||||
|
|
||||||
public double Top { get; set; }
|
|
||||||
|
|
||||||
public Color? BackgroundColor { get; init; }
|
|
||||||
|
|
||||||
#region Border
|
|
||||||
public Color? BorderColor { get; init; }
|
|
||||||
|
|
||||||
public string? BorderStyle { get; init; }
|
|
||||||
|
|
||||||
public int? BorderWidth { get; set; }
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public void Locate(IEnumerable<IAnnotation> annotations)
|
|
||||||
{
|
|
||||||
// set Top
|
|
||||||
if (annotations.MinBy(a => a.Top)?.Top is double minTop)
|
|
||||||
Top = minTop;
|
|
||||||
|
|
||||||
// set Left
|
|
||||||
if (annotations.MinBy(a => a.Left)?.Left is double minLeft)
|
|
||||||
Left = minLeft;
|
|
||||||
|
|
||||||
// set Width
|
|
||||||
if(annotations.MaxBy(a => a.GetRight())?.GetRight() is double maxRight)
|
|
||||||
Width = maxRight - Left;
|
|
||||||
|
|
||||||
// set Height
|
|
||||||
if (annotations.MaxBy(a => a.GetBottom())?.GetBottom() is double maxBottom)
|
|
||||||
Height = maxBottom - Top;
|
|
||||||
|
|
||||||
// add margins
|
|
||||||
Top -= Margin;
|
|
||||||
Left -= Margin;
|
|
||||||
Width += Margin * 2;
|
|
||||||
Height += Margin * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
|
||||||
|
|
||||||
public record Color
|
|
||||||
{
|
|
||||||
public int R { get; init; } = 0;
|
|
||||||
|
|
||||||
public int G { get; init; } = 0;
|
|
||||||
|
|
||||||
public int B { get; init; } = 0;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
|
||||||
|
|
||||||
public static class Extensions
|
|
||||||
{
|
|
||||||
public static double GetRight(this IAnnotation annotation) => annotation.Left + annotation?.Width ?? 0;
|
|
||||||
|
|
||||||
public static double GetBottom(this IAnnotation annotation) => annotation.Top + annotation?.Height ?? 0;
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
|
||||||
|
|
||||||
public interface IAnnotation
|
|
||||||
{
|
|
||||||
string Name { get; }
|
|
||||||
|
|
||||||
double? Width { get; }
|
|
||||||
|
|
||||||
double? Height { get; }
|
|
||||||
|
|
||||||
double Left { get; }
|
|
||||||
|
|
||||||
double Top { get; }
|
|
||||||
|
|
||||||
Color? BackgroundColor { get; }
|
|
||||||
|
|
||||||
Color? BorderColor { get; }
|
|
||||||
|
|
||||||
string? BorderStyle { get; }
|
|
||||||
|
|
||||||
int? BorderWidth { get; }
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API.Models;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the parameters for two-factor authentication (2FA) registration.
|
|
||||||
/// </summary>
|
|
||||||
public class TFARegParams
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The maximum allowed time for completing the registration process.
|
|
||||||
/// </summary>
|
|
||||||
public TimeSpan TimeLimit { get; init; } = new(0, 30, 0);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The deadline for registration, calculated as the current time plus the <see cref="TimeLimit"/>.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime Deadline => DateTime.Now.AddTicks(TimeLimit.Ticks);
|
|
||||||
}
|
|
||||||
@@ -1,259 +0,0 @@
|
|||||||
using DigitalData.Core.API;
|
|
||||||
using DigitalData.Core.Application;
|
|
||||||
using EnvelopeGenerator.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
||||||
using Microsoft.AspNetCore.Localization;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System.Globalization;
|
|
||||||
using Scalar.AspNetCore;
|
|
||||||
using Microsoft.OpenApi.Models;
|
|
||||||
using DigitalData.UserManager.DependencyInjection;
|
|
||||||
using EnvelopeGenerator.Application;
|
|
||||||
using DigitalData.Auth.Client;
|
|
||||||
using DigitalData.Core.Abstractions;
|
|
||||||
using EnvelopeGenerator.API.Models;
|
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
||||||
using Microsoft.IdentityModel.Tokens;
|
|
||||||
using DigitalData.Core.Abstractions.Security.Extensions;
|
|
||||||
using EnvelopeGenerator.API.Middleware;
|
|
||||||
using NLog.Web;
|
|
||||||
using NLog;
|
|
||||||
|
|
||||||
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
|
||||||
logger.Info("Logging initialized!");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
|
||||||
|
|
||||||
builder.Configuration.AddJsonFile("yarp.json", optional: true, reloadOnChange: true);
|
|
||||||
|
|
||||||
builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
|
|
||||||
|
|
||||||
if (!builder.Environment.IsDevelopment())
|
|
||||||
{
|
|
||||||
builder.Logging.ClearProviders();
|
|
||||||
builder.Host.UseNLog();
|
|
||||||
}
|
|
||||||
|
|
||||||
var config = builder.Configuration;
|
|
||||||
|
|
||||||
var deferredProvider = new DeferredServiceProvider();
|
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
|
||||||
builder.Services.AddHttpClient();
|
|
||||||
builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
|
|
||||||
|
|
||||||
// CORS Policy
|
|
||||||
var allowedOrigins = config.GetSection("AllowedOrigins").Get<string[]>() ??
|
|
||||||
throw new InvalidOperationException("AllowedOrigins section is missing in the configuration.");
|
|
||||||
builder.Services.AddCors(options =>
|
|
||||||
{
|
|
||||||
options.AddPolicy("AllowSpecificOriginsPolicy", builder =>
|
|
||||||
{
|
|
||||||
builder.WithOrigins(allowedOrigins)
|
|
||||||
.SetIsOriginAllowedToAllowWildcardSubdomains()
|
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowAnyHeader()
|
|
||||||
.AllowCredentials();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Swagger
|
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
|
||||||
builder.Services.AddSwaggerGen(options =>
|
|
||||||
{
|
|
||||||
options.SwaggerDoc("v1", new OpenApiInfo
|
|
||||||
{
|
|
||||||
Version = "v1",
|
|
||||||
Title = "signFLOW Absender-API",
|
|
||||||
Description = "Eine API zur Verwaltung der Erstellung, des Versands und der Nachverfolgung von Umschlägen in der signFLOW-Anwendung.",
|
|
||||||
Contact = new OpenApiContact
|
|
||||||
{
|
|
||||||
Name = "Digital Data GmbH",
|
|
||||||
Url = new Uri("https://digitaldata.works/digitale-signatur#kontakt"),
|
|
||||||
Email = "info-flow@digitaldata.works"
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
|
||||||
{
|
|
||||||
Name = "Authorization",
|
|
||||||
Type = SecuritySchemeType.Http,
|
|
||||||
Scheme = "bearer",
|
|
||||||
BearerFormat = "JWT",
|
|
||||||
In = ParameterLocation.Header,
|
|
||||||
Description = "JWT-Autorisierungs-Header unter Verwendung des Bearer-Schemas.",
|
|
||||||
});
|
|
||||||
|
|
||||||
options.AddSecurityRequirement(new OpenApiSecurityRequirement
|
|
||||||
{
|
|
||||||
{
|
|
||||||
new OpenApiSecurityScheme
|
|
||||||
{
|
|
||||||
Reference = new OpenApiReference
|
|
||||||
{
|
|
||||||
Type = ReferenceType.SecurityScheme,
|
|
||||||
Id = "Bearer"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Array.Empty<string>()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.xml");
|
|
||||||
foreach (var xmlFile in xmlFiles)
|
|
||||||
{
|
|
||||||
options.IncludeXmlComments(xmlFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
options.DocumentFilter<EnvelopeGenerator.API.Documentation.AuthProxyDocumentFilter>();
|
|
||||||
});
|
|
||||||
builder.Services.AddOpenApi();
|
|
||||||
|
|
||||||
//AddEF Core dbcontext
|
|
||||||
var useDbMigration = Environment.GetEnvironmentVariable("MIGRATION_TEST_MODE") == true.ToString() || config.GetValue<bool>("UseDbMigration");
|
|
||||||
var cnnStrName = useDbMigration ? "DbMigrationTest" : "Default";
|
|
||||||
var connStr = config.GetConnectionString(cnnStrName)
|
|
||||||
?? throw new InvalidOperationException($"Connection string '{cnnStrName}' is missing in the application configuration.");
|
|
||||||
|
|
||||||
builder.Services.Configure<ConnectionString>(cs => cs.Value = connStr);
|
|
||||||
|
|
||||||
builder.Services.AddDbContext<EGDbContext>(options => options.UseSqlServer(connStr));
|
|
||||||
|
|
||||||
builder.Services.AddAuthHubClient(config.GetSection("AuthClientParams"));
|
|
||||||
|
|
||||||
var authTokenKeys = config.GetOrDefault<AuthTokenKeys>();
|
|
||||||
|
|
||||||
builder.Services.AddAuthentication(options =>
|
|
||||||
{
|
|
||||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
||||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
||||||
})
|
|
||||||
.AddJwtBearer(opt =>
|
|
||||||
{
|
|
||||||
opt.TokenValidationParameters = new TokenValidationParameters
|
|
||||||
{
|
|
||||||
ValidateIssuerSigningKey = true,
|
|
||||||
IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) =>
|
|
||||||
{
|
|
||||||
var clientParams = deferredProvider.GetOptions<ClientParams>();
|
|
||||||
var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience);
|
|
||||||
return [publicKey.SecurityKey];
|
|
||||||
},
|
|
||||||
ValidateIssuer = true,
|
|
||||||
ValidIssuer = authTokenKeys.Issuer,
|
|
||||||
ValidateAudience = true,
|
|
||||||
ValidAudience = authTokenKeys.Audience,
|
|
||||||
};
|
|
||||||
|
|
||||||
opt.Events = new JwtBearerEvents
|
|
||||||
{
|
|
||||||
OnMessageReceived = context =>
|
|
||||||
{
|
|
||||||
// if there is no token read related cookie or query string
|
|
||||||
if (context.Token is null) // if there is no token
|
|
||||||
{
|
|
||||||
if (context.Request.Cookies.TryGetValue(authTokenKeys.Cookie, out var cookieToken) && cookieToken is not null)
|
|
||||||
context.Token = cookieToken;
|
|
||||||
else if (context.Request.Query.TryGetValue(authTokenKeys.QueryString, out var queryStrToken))
|
|
||||||
context.Token = queryStrToken;
|
|
||||||
}
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Authentication
|
|
||||||
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
|
||||||
.AddCookie(options =>
|
|
||||||
{
|
|
||||||
options.Cookie.HttpOnly = true; // Makes the cookie inaccessible to client-side scripts for security
|
|
||||||
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; // Ensures cookies are sent over HTTPS only
|
|
||||||
options.Cookie.SameSite = SameSiteMode.Strict; // Protects against CSRF attacks by restricting how cookies are sent with requests from external sites
|
|
||||||
options.LoginPath = "/api/auth/login";
|
|
||||||
options.LogoutPath = "/api/auth/logout";
|
|
||||||
options.SlidingExpiration = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// User manager
|
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
|
||||||
builder.Services.AddUserManager<EGDbContext>();
|
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
||||||
|
|
||||||
// LDAP
|
|
||||||
builder.ConfigureBySection<DirectorySearchOptions>();
|
|
||||||
builder.Services.AddDirectorySearchService(config.GetSection("DirectorySearchOptions"));
|
|
||||||
|
|
||||||
// Localizer
|
|
||||||
builder.Services.AddCookieBasedLocalizer();
|
|
||||||
|
|
||||||
// Envelope generator serives
|
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
|
||||||
builder.Services
|
|
||||||
.AddEnvelopeGeneratorInfrastructureServices(opt =>
|
|
||||||
{
|
|
||||||
opt.AddDbTriggerParams(config);
|
|
||||||
opt.AddDbContext((provider, options) =>
|
|
||||||
{
|
|
||||||
var logger = provider.GetRequiredService<ILogger<EGDbContext>>();
|
|
||||||
options.UseSqlServer(connStr)
|
|
||||||
.LogTo(log => logger.LogInformation("{log}", log), Microsoft.Extensions.Logging.LogLevel.Trace)
|
|
||||||
.EnableSensitiveDataLogging()
|
|
||||||
.EnableDetailedErrors();
|
|
||||||
});
|
|
||||||
opt.AddSQLExecutor(executor => executor.ConnectionString = connStr);
|
|
||||||
})
|
|
||||||
.AddEnvelopeGeneratorServices(config);
|
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
||||||
|
|
||||||
var app = builder.Build();
|
|
||||||
|
|
||||||
deferredProvider.Factory = () => app.Services;
|
|
||||||
|
|
||||||
app.UseMiddleware<ExceptionHandlingMiddleware>();
|
|
||||||
|
|
||||||
app.MapOpenApi();
|
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
|
||||||
if (app.Environment.IsDevelopment() || (app.IsDevOrDiP() && config.GetValue<bool>("UseSwagger")))
|
|
||||||
{
|
|
||||||
app.UseSwagger();
|
|
||||||
app.UseSwaggerUI();
|
|
||||||
app.MapScalarApiReference();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set CORS policy
|
|
||||||
app.UseCors("AllowSpecificOriginsPolicy");
|
|
||||||
|
|
||||||
// Localizer
|
|
||||||
string[] supportedCultureNames = ["de-DE", "en-US"];
|
|
||||||
IList<CultureInfo> list = [.. supportedCultureNames.Select(cn => new CultureInfo(cn))];
|
|
||||||
var cultureInfo = list.FirstOrDefault() ?? throw new InvalidOperationException("There is no supported culture.");
|
|
||||||
var requestLocalizationOptions = new RequestLocalizationOptions
|
|
||||||
{
|
|
||||||
SupportedCultures = list,
|
|
||||||
SupportedUICultures = list
|
|
||||||
};
|
|
||||||
requestLocalizationOptions.RequestCultureProviders.Add(new QueryStringRequestCultureProvider());
|
|
||||||
app.UseRequestLocalization(requestLocalizationOptions);
|
|
||||||
|
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
|
||||||
|
|
||||||
app.UseDefaultFiles();
|
|
||||||
app.UseStaticFiles();
|
|
||||||
|
|
||||||
app.UseAuthentication();
|
|
||||||
app.UseAuthorization();
|
|
||||||
|
|
||||||
app.MapReverseProxy();
|
|
||||||
app.MapControllers();
|
|
||||||
|
|
||||||
app.Run();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
logger.Error(ex, "Stopped program because of exception");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
|
||||||
"iisSettings": {
|
|
||||||
"windowsAuthentication": false,
|
|
||||||
"anonymousAuthentication": true,
|
|
||||||
"iisExpress": {
|
|
||||||
"applicationUrl": "http://localhost:4176",
|
|
||||||
"sslPort": 44300
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"profiles": {
|
|
||||||
"http": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "swagger",
|
|
||||||
"applicationUrl": "http://localhost:5131",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"https": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": false,
|
|
||||||
"launchUrl": "swagger",
|
|
||||||
"applicationUrl": "https://localhost:8088;http://localhost:5131",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"angular": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "",
|
|
||||||
"applicationUrl": "https://localhost:7174;http://localhost:5131",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"IIS Express": {
|
|
||||||
"commandName": "IISExpress",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "swagger",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AuthClientParams": {
|
|
||||||
"Url": "http://172.24.12.39:9090/auth-hub",
|
|
||||||
"PublicKeys": [
|
|
||||||
{
|
|
||||||
"Issuer": "auth.digitaldata.works",
|
|
||||||
"Audience": "sign-flow.digitaldata.works"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"RetryDelay": "00:00:05"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
{
|
|
||||||
"UseSwagger": true,
|
|
||||||
"UseDbMigration": true,
|
|
||||||
"DiPMode": true,
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowedHosts": "*",
|
|
||||||
"AllowedOrigins": [ "http://localhost:4200", "http://172.24.12.39:9090", "https://localhost:8088", "http://localhost:5131", "http://localhost:7192" ],
|
|
||||||
"ConnectionStrings": {
|
|
||||||
"Default": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;",
|
|
||||||
"DbMigrationTest": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM_DATA_MIGR_TEST;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
|
|
||||||
},
|
|
||||||
"DirectorySearchOptions": {
|
|
||||||
"ServerName": "DD-VMP01-DC01",
|
|
||||||
"Root": "DC=dd-gan,DC=local,DC=digitaldata,DC=works",
|
|
||||||
"UserCacheExpirationDays": 1,
|
|
||||||
"CustomSearchFilters": {
|
|
||||||
"User": "(&(objectClass=user)(sAMAccountName=*))",
|
|
||||||
"Group": "(&(objectClass=group)(samAccountName=*))"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AuthClientParams": {
|
|
||||||
"Url": "http://172.24.12.39:9090/auth-hub",
|
|
||||||
"PublicKeys": [
|
|
||||||
{
|
|
||||||
"Issuer": "auth.digitaldata.works",
|
|
||||||
"Audience": "sign-flow.digitaldata.works"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"RetryDelay": "00:00:05"
|
|
||||||
},
|
|
||||||
"AuthTokenKeys": {
|
|
||||||
"Cookie": "AuthToken",
|
|
||||||
"QueryString": "AuthToken",
|
|
||||||
"Issuer": "auth.digitaldata.works",
|
|
||||||
"Audience": "sign-flow.digitaldata.works"
|
|
||||||
},
|
|
||||||
"PSPDFKitLicenseKey": "SXCtGGY9XA-31OGUXQK-r7c6AkdLGPm2ljuyDr1qu0kkhLvydg-Do-fxpNUF4Rq3fS_xAnZRNFRHbXpE6sQ2BMcCSVTcXVJO6tPviexjpiT-HnrDEySlUERJnnvh-tmeOWprxS6BySPnSILkmaVQtUfOIUS-cUbvvEYHTvQBKbSF8di4XHQFyfv49ihr51axm3NVV3AXwh2EiKL5C5XdqBZ4sQ4O7vXBjM2zvxdPxlxdcNYmiU83uAzw7B83O_jubPzya4CdUHh_YH7Nlp2gP56MeG1Sw2JhMtfG3Rj14Sg4ctaeL9p6AEWca5dDjJ2li5tFIV2fQSsw6A_cowLu0gtMm5i8IfJXeIcQbMC2-0wGv1oe9hZYJvFMdzhTM_FiejM0agemxt3lJyzuyP8zbBSOgp7Si6A85krLWPZptyZBTG7pp7IHboUHfPMxCXqi-zMsqewOJtQBE2mjntU-lPryKnssOpMPfswwQX7QSkJYV5EMqNmEhQX6mEkp2wcqFzMC7bJQew1aO4pOpvChUaMvb1vgRek0HxLag0nwQYX2YrYGh7F_xXJs-8HNwJe8H0-eW4x4faayCgM5rB5772CCCsD9ThZcvXFrjNHHLGJ8WuBUFm6LArvSfFQdii_7j-_sqHMpeKZt26NFgivj1A==",
|
|
||||||
"Content-Security-Policy": [ // The first format parameter {0} will be replaced by the nonce value.
|
|
||||||
"default-src 'self'",
|
|
||||||
"script-src 'self' 'nonce-{0}' 'unsafe-eval'",
|
|
||||||
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com:*",
|
|
||||||
"img-src 'self' data: https: blob:",
|
|
||||||
"font-src 'self' https://fonts.gstatic.com:*",
|
|
||||||
"connect-src 'self' https://nominatim.openstreetmap.org:* http://localhost:* https://localhost:* ws://localhost:* wss://localhost:* blob:",
|
|
||||||
"frame-src 'self'",
|
|
||||||
"media-src 'self'",
|
|
||||||
"object-src 'self'"
|
|
||||||
],
|
|
||||||
"NLog": {
|
|
||||||
"throwConfigExceptions": true,
|
|
||||||
"variables": {
|
|
||||||
"logDirectory": "E:\\LogFiles\\Digital Data\\signFlow",
|
|
||||||
"logFileNamePrefix": "${shortdate}-ECM.EnvelopeGenerator.Web"
|
|
||||||
},
|
|
||||||
"targets": {
|
|
||||||
"infoLogs": {
|
|
||||||
"type": "File",
|
|
||||||
"fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log",
|
|
||||||
"maxArchiveDays": 30
|
|
||||||
},
|
|
||||||
"errorLogs": {
|
|
||||||
"type": "File",
|
|
||||||
"fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log",
|
|
||||||
"maxArchiveDays": 30
|
|
||||||
},
|
|
||||||
"criticalLogs": {
|
|
||||||
"type": "File",
|
|
||||||
"fileName": "${logDirectory}\\${logFileNamePrefix}-Critical.log",
|
|
||||||
"maxArchiveDays": 30
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Trace, Debug, Info, Warn, Error and *Fatal*
|
|
||||||
"rules": [
|
|
||||||
{
|
|
||||||
"logger": "*",
|
|
||||||
"minLevel": "Info",
|
|
||||||
"maxLevel": "Warn",
|
|
||||||
"writeTo": "infoLogs"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"logger": "*",
|
|
||||||
"level": "Error",
|
|
||||||
"writeTo": "errorLogs"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"logger": "*",
|
|
||||||
"level": "Fatal",
|
|
||||||
"writeTo": "criticalLogs"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ContactLink": {
|
|
||||||
"Label": "Kontakt",
|
|
||||||
"Href": "https://digitaldata.works/",
|
|
||||||
"HrefLang": "de",
|
|
||||||
"Target": "_blank",
|
|
||||||
"Title": "Digital Data GmbH"
|
|
||||||
},
|
|
||||||
/* Resx naming format is -> Resource.language.resx (eg: Resource.de_DE.resx).
|
|
||||||
To add a new language, first you should write the required resx file.
|
|
||||||
first is the default culture name. */
|
|
||||||
"Cultures": [
|
|
||||||
{
|
|
||||||
"Language": "de-DE",
|
|
||||||
"FIClass": "fi-de"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Language": "en-US",
|
|
||||||
"FIClass": "fi-us"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"DisableMultiLanguage": false,
|
|
||||||
"Regexes": [
|
|
||||||
{
|
|
||||||
"Pattern": "/^\\p{L}+(?:([\\ \\-\\']|(\\.\\ ))\\p{L}+)*$/u",
|
|
||||||
"Name": "City",
|
|
||||||
"Platforms": [ ".NET" ]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Pattern": "/^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$/",
|
|
||||||
"Name": "City",
|
|
||||||
"Platforms": [ "javascript" ]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"CustomImages": {
|
|
||||||
"App": {
|
|
||||||
"Src": "/img/DD_signFLOW_LOGO.png",
|
|
||||||
"Classes": {
|
|
||||||
"Main": "signFlow-logo"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Company": {
|
|
||||||
"Src": "/img/digital_data.svg",
|
|
||||||
"Classes": {
|
|
||||||
"Show": "dd-show-logo",
|
|
||||||
"Locked": "dd-locked-logo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"DispatcherParams": {
|
|
||||||
"SendingProfile": 1,
|
|
||||||
"AddedWho": "DDEnvelopGenerator",
|
|
||||||
"ReminderTypeId": 202377,
|
|
||||||
"EmailAttmt1": ""
|
|
||||||
},
|
|
||||||
"MailParams": {
|
|
||||||
"Placeholders": {
|
|
||||||
"[NAME_PORTAL]": "signFlow",
|
|
||||||
"[SIGNATURE_TYPE]": "signieren",
|
|
||||||
"[REASON]": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"GtxMessagingParams": {
|
|
||||||
"Uri": "https://rest.gtx-messaging.net",
|
|
||||||
"Path": "smsc/sendsms/f566f7e5-bdf2-4a9a-bf52-ed88215a432e/json",
|
|
||||||
"Headers": {},
|
|
||||||
"QueryParams": {
|
|
||||||
"from": "signFlow"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"TFARegParams": {
|
|
||||||
"TimeLimit": "00:30:00"
|
|
||||||
},
|
|
||||||
"DbTriggerParams": {
|
|
||||||
"Envelope": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ],
|
|
||||||
"EnvelopeHistory": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ],
|
|
||||||
"EmailOut": [ "TBEMLP_EMAIL_OUT_AFT_INS", "TBEMLP_EMAIL_OUT_AFT_UPD" ],
|
|
||||||
"EnvelopeReceiverReadOnly": [ "TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD" ],
|
|
||||||
"Receiver": [],
|
|
||||||
"EmailTemplate": [ "TBSIG_EMAIL_TEMPLATE_AFT_UPD" ]
|
|
||||||
},
|
|
||||||
"MainPageTitle": null,
|
|
||||||
"AnnotationParams": {
|
|
||||||
"Background": {
|
|
||||||
"Margin": 0.20,
|
|
||||||
"BackgroundColor": {
|
|
||||||
"R": 222,
|
|
||||||
"G": 220,
|
|
||||||
"B": 215
|
|
||||||
},
|
|
||||||
"BorderColor": {
|
|
||||||
"R": 204,
|
|
||||||
"G": 202,
|
|
||||||
"B": 198
|
|
||||||
},
|
|
||||||
"BorderStyle": "underline",
|
|
||||||
"BorderWidth": 4
|
|
||||||
},
|
|
||||||
"DefaultAnnotation": {
|
|
||||||
"Width": 1,
|
|
||||||
"Height": 0.5,
|
|
||||||
"MarginTop": 1
|
|
||||||
},
|
|
||||||
"Annotations": [
|
|
||||||
{
|
|
||||||
"Name": "Signature",
|
|
||||||
"MarginTop": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Name": "PositionLabel",
|
|
||||||
"VerBoundAnnotName": "Signature",
|
|
||||||
"WidthRatio": 1.2,
|
|
||||||
"HeightRatio": 0.5,
|
|
||||||
"MarginTopRatio": 0.22
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Name": "Position",
|
|
||||||
"VerBoundAnnotName": "PositionLabel",
|
|
||||||
"WidthRatio": 1.2,
|
|
||||||
"HeightRatio": 0.5,
|
|
||||||
"MarginTopRatio": -0.05
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Name": "CityLabel",
|
|
||||||
"VerBoundAnnotName": "Position",
|
|
||||||
"WidthRatio": 1.2,
|
|
||||||
"HeightRatio": 0.5,
|
|
||||||
"MarginTopRatio": 0.05
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Name": "City",
|
|
||||||
"VerBoundAnnotName": "CityLabel",
|
|
||||||
"WidthRatio": 1.2,
|
|
||||||
"HeightRatio": 0.5,
|
|
||||||
"MarginTopRatio": -0.05
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Name": "DateLabel",
|
|
||||||
"VerBoundAnnotName": "City",
|
|
||||||
"WidthRatio": 1.55,
|
|
||||||
"HeightRatio": 0.5,
|
|
||||||
"MarginTopRatio": 0.05
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Name": "Date",
|
|
||||||
"VerBoundAnnotName": "DateLabel",
|
|
||||||
"WidthRatio": 1.55,
|
|
||||||
"HeightRatio": 0.5,
|
|
||||||
"MarginTopRatio": -0.1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"ReverseProxy": {
|
|
||||||
"Routes": {
|
|
||||||
"auth-login": {
|
|
||||||
"ClusterId": "auth-hub",
|
|
||||||
"Match": {
|
|
||||||
"Path": "/api/auth",
|
|
||||||
"Methods": [ "POST" ]
|
|
||||||
},
|
|
||||||
"Transforms": [
|
|
||||||
{ "PathSet": "/api/auth/sign-flow" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Clusters": {
|
|
||||||
"auth-hub": {
|
|
||||||
"Destinations": {
|
|
||||||
"primary": {
|
|
||||||
"Address": "http://172.24.12.39:9090"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class AuthenticatorParams
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789012345678901234567890123456789'
|
|
||||||
/// </summary>
|
|
||||||
public string CharPool { get; init; } = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789012345678901234567890123456789";
|
|
||||||
|
|
||||||
//TODO: Increase the DefaultTotpSecretKeyLength (e.g. to 32) but make sure that the QR code is generated correctly and can be scanned by the authenticator.
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 20
|
|
||||||
/// </summary>
|
|
||||||
public int DefaultTotpSecretKeyLength { get; init; } = 20;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is
|
|
||||||
/// </summary>
|
|
||||||
public string TotpIssuer { get; init; } = "signFlow";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 0 is user email, 1 is secret key and 2 is issuer.
|
|
||||||
/// </summary>
|
|
||||||
public string TotpUrlFormat { get; init; } = "otpauth://totp/{0}?secret={1}&issuer={2}";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 20.
|
|
||||||
/// </summary>
|
|
||||||
public int TotpQRPixelsPerModule { get; init; } = 20;
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class DispatcherParams
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 1
|
|
||||||
/// </summary>
|
|
||||||
public int SendingProfile { get; init; } = 1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defalt value is 'DDEnvelopGenerator'
|
|
||||||
/// </summary>
|
|
||||||
public string AddedWho { get; init; } = "DDEnvelopGenerator";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 202377
|
|
||||||
/// </summary>
|
|
||||||
public int ReminderTypeId { get; init; } = 202377;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is string.Empty
|
|
||||||
/// </summary>
|
|
||||||
public string? EmailAttmt1 { get; init; } = null;
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
using DigitalData.Core.Client.Interface;
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// https://www.gtx-messaging.com/en/api-docs/sms-rest-api/
|
|
||||||
/// </summary>
|
|
||||||
public class GtxMessagingParams : IHttpClientOptions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required string Uri { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? Path { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, object>? Headers { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, object?>? QueryParams { get; init; }
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'to'
|
|
||||||
/// </summary>
|
|
||||||
public string RecipientQueryParamName { get; init; } = "to";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'text'
|
|
||||||
/// </summary>
|
|
||||||
public string MessageQueryParamName { get; init; } = "text";
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class MailParams
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required Dictionary<string, string> Placeholders { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
using OtpNet;
|
|
||||||
using System.Globalization;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class TotpSmsParams
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The unit is second.
|
|
||||||
/// </summary>
|
|
||||||
public int TotpStep { get; init; } = 90;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'Ihr 2FA-Passwort lautet {0}. Gültig bis {1}'
|
|
||||||
/// </summary>
|
|
||||||
public string Format { get; init; } = "Ihr 2FA-Passwort lautet {0}. Gültig bis {1}";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public ExpirationHandler Expiration { get; init; } = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is VerificationWindow.RfcSpecifiedNetworkDelay
|
|
||||||
/// </summary>
|
|
||||||
public VerificationWindow? TotpVerificationWindow { get; private init; } = VerificationWindow.RfcSpecifiedNetworkDelay;
|
|
||||||
|
|
||||||
private IEnumerable<int>? _tvwParams;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<int>? TotpVerificationWindowParams
|
|
||||||
{
|
|
||||||
get => _tvwParams;
|
|
||||||
init
|
|
||||||
{
|
|
||||||
_tvwParams = value;
|
|
||||||
if(_tvwParams is not null)
|
|
||||||
TotpVerificationWindow = new(previous: _tvwParams.ElementAtOrDefault(0), future: _tvwParams.ElementAtOrDefault(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class ExpirationHandler
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'e{0}_r{1}_sms_code_expiration'
|
|
||||||
/// </summary>
|
|
||||||
public string CacheKeyFormat { get; init; } = "e{0}_r{1}_sms_code_expiration";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is HH:mm:ss
|
|
||||||
/// </summary>
|
|
||||||
public string Format { get; init; } = "HH:mm:ss";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'de-DE'
|
|
||||||
/// </summary>
|
|
||||||
public string CultureName
|
|
||||||
{
|
|
||||||
get => _cultureInfo.Name;
|
|
||||||
init => _cultureInfo = new(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'de-DE'
|
|
||||||
/// </summary>
|
|
||||||
private CultureInfo _cultureInfo = new("de-DE");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'de-DE'
|
|
||||||
/// </summary>
|
|
||||||
public CultureInfo CultureInfo => _cultureInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public record AnnotationCreateDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int ElementId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string Name { get; init; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string Value { get; init; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string Type { get; init; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public double? X { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public double? Y { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public double? Width { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public double? Height { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public record AnnotationDto : AnnotationCreateDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public long Id { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? ChangedWho { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object representing configuration settings.
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public class ConfigDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the sending profile identifier.
|
|
||||||
/// </summary>
|
|
||||||
public int SendingProfile { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the signature host URL or name.
|
|
||||||
/// </summary>
|
|
||||||
public required string SignatureHost { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the name of the external program.
|
|
||||||
/// </summary>
|
|
||||||
public string? ExternalProgramName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the path where exports will be saved.
|
|
||||||
/// </summary>
|
|
||||||
public string? ExportPath { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object representing a document within an envelope, including optional binary data and form elements.
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public class DocumentDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the unique identifier of the document.
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the envelope ID to which the document belongs.
|
|
||||||
/// </summary>
|
|
||||||
public int EnvelopeId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the date and time when the document was added.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the binary data of the document, if available.
|
|
||||||
/// </summary>
|
|
||||||
public byte[]? ByteData { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the collection of elements associated with the document for receiver interactions, if any.
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<SignatureDto>? Elements { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object representing the status of a document for a specific receiver.
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public class DocumentStatusDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the unique identifier of the document status entry.
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the ID of the associated envelope.
|
|
||||||
/// </summary>
|
|
||||||
public int EnvelopeId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the ID of the receiver associated with this status.
|
|
||||||
/// </summary>
|
|
||||||
public int ReceiverId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the current status code.
|
|
||||||
/// </summary>
|
|
||||||
public EnvelopeStatus Status { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the timestamp when the status was changed.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? StatusChangedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the timestamp when this record was added.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the timestamp when this record was last changed.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the display value associated with the status.
|
|
||||||
/// </summary>
|
|
||||||
public string? Value { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public record EmailTemplateDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int Id{ get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required string Name { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required string Body { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required string Subject { get; set; }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
|
|
||||||
using DigitalData.UserManager.Application.DTOs.User;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public record EnvelopeDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int UserId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required EnvelopeStatus Status { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is string.Empty
|
|
||||||
/// </summary>
|
|
||||||
public string StatusName { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is string.Empty
|
|
||||||
/// </summary>
|
|
||||||
public string Uuid { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is string.Empty
|
|
||||||
/// </summary>
|
|
||||||
[TemplatePlaceholder("[MESSAGE]")]
|
|
||||||
public string Message { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is string.Empty
|
|
||||||
/// </summary>
|
|
||||||
[TemplatePlaceholder("[DOCUMENT_TITLE]")]
|
|
||||||
public string Title { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int? ContractType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'de-DE'
|
|
||||||
/// </summary>
|
|
||||||
public string Language { get; set; } = "de-DE";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int? EnvelopeTypeId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public bool ReadOnly => EnvelopeTypeId == 2;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int? CertificationType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public bool UseAccessCode { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public bool TFAEnabled { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public UserReadDto? User { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public EnvelopeType? EnvelopeType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? EnvelopeTypeTitle { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public bool IsAlreadySent { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public byte[]? DocResult { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<DocumentDto>? Documents { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public record EnvelopeReceiverDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public EnvelopeDto? Envelope { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public ReceiverDto? Receiver { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public (int Envelope, int Receiver) Id => (Envelope: EnvelopeId, Receiver: ReceiverId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int EnvelopeId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int ReceiverId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int Sequence { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[TemplatePlaceholder("[NAME_RECEIVER]")]
|
|
||||||
public string? Name { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? JobTitle { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? CompanyName { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? PrivateMessage { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public bool HasPhoneNumber { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public record EnvelopeReceiverSecretDto : EnvelopeReceiverDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? AccessCode { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? PhoneNumber { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="DateValid"></param>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public record EnvelopeReceiverReadOnlyCreateDto(
|
|
||||||
DateTime DateValid)
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[EmailAddress]
|
|
||||||
[Required]
|
|
||||||
public required string ReceiverMail { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is null
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
public long? EnvelopeId { get; set; } = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
public string? AddedWho { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is DateTime.Now
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
public DateTime AddedWhen { get; } = DateTime.Now;
|
|
||||||
};
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a read-only Data Transfer Object (DTO) for an envelope receiver.
|
|
||||||
/// Contains information about the receiver, associated envelope, and audit details.
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public class EnvelopeReceiverReadOnlyDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the unique identifier of the envelope receiver.
|
|
||||||
/// </summary>
|
|
||||||
public long Id { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the identifier of the associated envelope.
|
|
||||||
/// </summary>
|
|
||||||
public long EnvelopeId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the email address of the receiver.
|
|
||||||
/// </summary>
|
|
||||||
public required string ReceiverMail { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the date until which the receiver is valid.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime DateValid { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the date and time when the receiver was added.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the user who added the receiver.
|
|
||||||
/// Default value is 'unknown'.
|
|
||||||
/// </summary>
|
|
||||||
public string AddedWho { get; init; } = "Unknown";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the associated envelope details.
|
|
||||||
/// </summary>
|
|
||||||
public EnvelopeDto? Envelope { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the user who last changed the receiver, if any.
|
|
||||||
/// </summary>
|
|
||||||
public string? ChangedWho { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the date and time when the receiver was last changed, if any.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the associated receiver details.
|
|
||||||
/// </summary>
|
|
||||||
public ReceiverDto? Receiver { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object for updating a read-only envelope receiver.
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public class EnvelopeReceiverReadOnlyUpdateDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the unique identifier of the envelope receiver.
|
|
||||||
/// </summary>
|
|
||||||
public long Id { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the date when the envelope receiver becomes valid.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime DateValid { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the name of the user who made the change.
|
|
||||||
/// Default value is unknown.
|
|
||||||
/// </summary>
|
|
||||||
public string ChangedWho { get; set; } = "Unknown";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the date and time when the change was made.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime ChangedWhen { get; set; } = DateTime.Now;
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object representing a type of envelope with its configuration settings.
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public class EnvelopeTypeDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the unique identifier of the envelope type.
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the title of the envelope type.
|
|
||||||
/// </summary>
|
|
||||||
public string Title { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the language code used in this envelope type.
|
|
||||||
/// </summary>
|
|
||||||
public string Language { get; set; } = "de-DE";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the number of days after which the envelope expires.
|
|
||||||
/// </summary>
|
|
||||||
public int? ExpiresDays { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the certification type identifier.
|
|
||||||
/// </summary>
|
|
||||||
public int? CertificationType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether an access code is required.
|
|
||||||
/// </summary>
|
|
||||||
public bool? UseAccessCode { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the final email template ID to be sent to the creator.
|
|
||||||
/// </summary>
|
|
||||||
public int? FinalEmailToCreator { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the final email template ID to be sent to the receivers.
|
|
||||||
/// </summary>
|
|
||||||
public int? FinalEmailToReceivers { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the timestamp when this envelope type was added.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the timestamp when this envelope type was last changed.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the number of days before expiry when a warning should be sent.
|
|
||||||
/// </summary>
|
|
||||||
public int? ExpiresWarningDays { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether reminder emails should be sent.
|
|
||||||
/// </summary>
|
|
||||||
public bool? SendReminderEmails { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the number of days before the first reminder is sent.
|
|
||||||
/// </summary>
|
|
||||||
public int? FirstReminderDays { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the interval in days between reminder emails.
|
|
||||||
/// </summary>
|
|
||||||
public int? ReminderIntervalDays { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the contract type associated with the envelope type.
|
|
||||||
/// </summary>
|
|
||||||
public int? ContractType { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto.History;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object for creating a new envelope history record.
|
|
||||||
/// </summary>
|
|
||||||
public class HistoryCreateDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the identifier of the envelope.
|
|
||||||
/// </summary>
|
|
||||||
public int EnvelopeId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the user reference associated with the action.
|
|
||||||
/// </summary>
|
|
||||||
public required string UserReference { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the status of the envelope at the time of the action.
|
|
||||||
/// </summary>
|
|
||||||
public int Status { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the date and time when the action occurred.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ActionDate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets an optional comment related to the action.
|
|
||||||
/// </summary>
|
|
||||||
public string? Comment { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
using DigitalData.UserManager.Application.DTOs.User;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.History;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object representing the history of an envelope, including status, sender, receiver, and related metadata.
|
|
||||||
/// </summary>
|
|
||||||
public record HistoryDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Unique identifier for the envelope history entry.
|
|
||||||
/// </summary>
|
|
||||||
public long Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Identifier of the associated envelope.
|
|
||||||
/// </summary>
|
|
||||||
public int EnvelopeId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reference string for the user related to this history entry.
|
|
||||||
/// </summary>
|
|
||||||
public required string UserReference { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Include code of the envelope at this history point.
|
|
||||||
/// </summary>
|
|
||||||
public EnvelopeStatus Status { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Type of reference for this history entry.
|
|
||||||
/// </summary>
|
|
||||||
public ReferenceType ReferenceType => ((int)Status).ToString().FirstOrDefault() switch
|
|
||||||
{
|
|
||||||
'1' => ReferenceType.Sender,
|
|
||||||
'2' => ReferenceType.Receiver,
|
|
||||||
_ => ReferenceType.System,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Human-readable name of the status.
|
|
||||||
/// </summary>
|
|
||||||
public string? StatusName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Date and time when this history entry was added.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Date and time when an action was performed, if applicable.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ActionDate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Information about the sender associated with this history entry.
|
|
||||||
/// </summary>
|
|
||||||
public UserCreateDto? Sender { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Information about the receiver associated with this history entry.
|
|
||||||
/// </summary>
|
|
||||||
public ReceiverDto? Receiver { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Optional comment related to this history entry.
|
|
||||||
/// </summary>
|
|
||||||
public string? Comment { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override int GetHashCode() => Id.GetHashCode();
|
|
||||||
};
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.History;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.Messaging;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the AutoMapper profile configuration for mapping between
|
|
||||||
/// domain entities and data transfer objects (DTOs) used within the EnvelopeGenerator application.
|
|
||||||
/// </summary>
|
|
||||||
public class MappingProfile : Profile
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="MappingProfile"/> class.
|
|
||||||
/// Configures the mappings between entities and DTOs used throughout the application.
|
|
||||||
/// </summary>
|
|
||||||
public MappingProfile()
|
|
||||||
{
|
|
||||||
// Entity to DTO mappings
|
|
||||||
CreateMap<Config, ConfigDto>();
|
|
||||||
CreateMap<Signature, SignatureDto>();
|
|
||||||
CreateMap<DocumentStatus, DocumentStatusDto>();
|
|
||||||
CreateMap<EmailTemplate, EmailTemplateDto>();
|
|
||||||
CreateMap<Envelope, EnvelopeDto>();
|
|
||||||
CreateMap<Document, DocumentDto>();
|
|
||||||
CreateMap<Domain.Entities.History, HistoryDto>();
|
|
||||||
CreateMap<Domain.Entities.History, HistoryCreateDto>();
|
|
||||||
CreateMap<Domain.Entities.EnvelopeReceiver, EnvelopeReceiverDto>();
|
|
||||||
CreateMap<Domain.Entities.EnvelopeReceiver, EnvelopeReceiverSecretDto>();
|
|
||||||
CreateMap<EnvelopeType, EnvelopeTypeDto>();
|
|
||||||
CreateMap<Domain.Entities.Receiver, ReceiverDto>();
|
|
||||||
CreateMap<Domain.Entities.EnvelopeReceiverReadOnly, EnvelopeReceiverReadOnlyDto>();
|
|
||||||
CreateMap<ElementAnnotation, AnnotationDto>();
|
|
||||||
|
|
||||||
// DTO to Entity mappings
|
|
||||||
CreateMap<ConfigDto, Config>();
|
|
||||||
CreateMap<SignatureDto, Signature>();
|
|
||||||
CreateMap<DocumentStatusDto, DocumentStatus>();
|
|
||||||
CreateMap<EmailTemplateDto, EmailTemplate>();
|
|
||||||
CreateMap<EnvelopeDto, Envelope>();
|
|
||||||
CreateMap<DocumentDto, Document>();
|
|
||||||
CreateMap<HistoryDto, Domain.Entities.History>();
|
|
||||||
CreateMap<HistoryCreateDto, Domain.Entities.History>();
|
|
||||||
CreateMap<EnvelopeReceiverDto, Domain.Entities.EnvelopeReceiver>();
|
|
||||||
CreateMap<EnvelopeTypeDto, EnvelopeType>();
|
|
||||||
CreateMap<ReceiverDto, Domain.Entities.Receiver>().ForMember(rcv => rcv.EnvelopeReceivers, rcvReadDto => rcvReadDto.Ignore());
|
|
||||||
CreateMap<EnvelopeReceiverReadOnlyCreateDto, Domain.Entities.EnvelopeReceiverReadOnly>();
|
|
||||||
CreateMap<EnvelopeReceiverReadOnlyUpdateDto, Domain.Entities.EnvelopeReceiverReadOnly>();
|
|
||||||
CreateMap<AnnotationCreateDto, ElementAnnotation>()
|
|
||||||
.ForMember(dest => dest.AddedWhen, opt => opt.MapFrom(_ => DateTime.UtcNow));
|
|
||||||
|
|
||||||
// Messaging mappings
|
|
||||||
// for GTX messaging
|
|
||||||
CreateMap<GtxMessagingResponse, SmsResponse>()
|
|
||||||
.ConstructUsing(gtxRes => gtxRes.Ok()
|
|
||||||
? new SmsResponse() { Ok = true }
|
|
||||||
: new SmsResponse() { Ok = false, Errors = gtxRes });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.Messaging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public class GtxMessagingResponse : Dictionary<string, object?> { }
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.Messaging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public record SmsResponse
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required bool Ok { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns !Ok
|
|
||||||
/// </summary>
|
|
||||||
public bool Failed => !Ok;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public dynamic? Errors { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.Receiver;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public class ReceiverDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required string EmailAddress { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required string Signature { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
public string? TotpSecretkey { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
public IEnumerable<EnvelopeReceiverDto>? EnvelopeReceivers { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? LastUsedName => EnvelopeReceivers?.LastOrDefault()?.Name;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? TfaRegDeadline { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return Id.GetHashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
using EnvelopeGenerator.Domain.Interfaces;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object representing a positioned element assigned to a document receiver.
|
|
||||||
/// </summary>
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public class SignatureDto : ISignature
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the unique identifier of the element.
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the identifier of the associated document.
|
|
||||||
/// </summary>
|
|
||||||
public int DocumentId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the identifier of the receiver.
|
|
||||||
/// </summary>
|
|
||||||
public int ReceiverId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the type of the element.
|
|
||||||
/// </summary>
|
|
||||||
public int ElementType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the X coordinate of the element.
|
|
||||||
/// </summary>
|
|
||||||
public double X { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the Y coordinate of the element.
|
|
||||||
/// </summary>
|
|
||||||
public double Y { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the width of the element.
|
|
||||||
/// </summary>
|
|
||||||
public double Width { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the height of the element.
|
|
||||||
/// </summary>
|
|
||||||
public double Height { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the page number where the element appears.
|
|
||||||
/// </summary>
|
|
||||||
public int Page { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether the element is required.
|
|
||||||
/// </summary>
|
|
||||||
public bool Required { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the tooltip text for the element.
|
|
||||||
/// </summary>
|
|
||||||
public string? Tooltip { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether the element is read-only.
|
|
||||||
/// </summary>
|
|
||||||
public bool ReadOnly { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the annotation index for ordering or reference.
|
|
||||||
/// </summary>
|
|
||||||
public int AnnotationIndex { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the timestamp when the element was added.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the timestamp when the element was last changed, if applicable.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the top position of the element (in layout terms).
|
|
||||||
/// </summary>
|
|
||||||
public double Top => Y;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the left position of the element (in layout terms).
|
|
||||||
/// </summary>
|
|
||||||
public double Left => X;
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public enum EnvelopeFlag
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
EnvelopeOrReceiverNonexists,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
NonDecodableEnvelopeReceiverId,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
WrongEnvelopeReceiverId,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
AccessCodeNull
|
|
||||||
}
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
using Microsoft.Extensions.Caching.Distributed;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class CacheExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Task SetLongAsync(this IDistributedCache cache, string key, long value, DistributedCacheEntryOptions? options = null, CancellationToken cToken = default)
|
|
||||||
=> options is null
|
|
||||||
? cache.SetAsync(key, BitConverter.GetBytes(value), token: cToken)
|
|
||||||
: cache.SetAsync(key, BitConverter.GetBytes(value), options: options, token: cToken);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<long?> GetLongAsync(this IDistributedCache cache, string key, CancellationToken cToken = default)
|
|
||||||
{
|
|
||||||
var value = await cache.GetAsync(key, cToken);
|
|
||||||
return value is null ? null : BitConverter.ToInt64(value, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Task SetDateTimeAsync(this IDistributedCache cache, string key, DateTime value, DistributedCacheEntryOptions? options = null, CancellationToken cToken = default)
|
|
||||||
=> cache.SetLongAsync(key: key, value: value.Ticks, options: options, cToken: cToken);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<DateTime?> GetDateTimeAsync(this IDistributedCache cache, string key, CancellationToken cToken = default)
|
|
||||||
{
|
|
||||||
var value = await cache.GetAsync(key, cToken);
|
|
||||||
return value is null ? null : new(BitConverter.ToInt64(value, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Task SetTimeSpanAsync(this IDistributedCache cache, string key, TimeSpan value, DistributedCacheEntryOptions? options = null, CancellationToken cToken = default)
|
|
||||||
=> cache.SetLongAsync(key: key, value: value.Ticks, options: options, cToken);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<TimeSpan?> GetTimeSpanAsync(this IDistributedCache cache, string key, CancellationToken cToken = default)
|
|
||||||
{
|
|
||||||
var value = await cache.GetAsync(key, cToken);
|
|
||||||
return value is null ? null : new(BitConverter.ToInt64(value, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: use code generator
|
|
||||||
#region GetOrSetAsync
|
|
||||||
|
|
||||||
#region string
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="factory"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <param name="cacheInBackground"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<string> GetOrSetAsync(this IDistributedCache cache, string key, Func<string> factory, DistributedCacheEntryOptions? options = null, bool cacheInBackground = false, CancellationToken cToken = default)
|
|
||||||
{
|
|
||||||
var value = await cache.GetStringAsync(key, cToken);
|
|
||||||
if (value is null)
|
|
||||||
{
|
|
||||||
// create new and save
|
|
||||||
value = factory();
|
|
||||||
|
|
||||||
Task CacheAsync() => options is null
|
|
||||||
? cache.SetStringAsync(key, value, cToken)
|
|
||||||
: cache.SetStringAsync(key, value, options, cToken);
|
|
||||||
|
|
||||||
if (cacheInBackground)
|
|
||||||
_ = Task.Run(async () => await CacheAsync(), cToken);
|
|
||||||
else
|
|
||||||
await CacheAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="factoryAsync"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <param name="cacheInBackground"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<string> GetOrSetAsync(this IDistributedCache cache, string key, Func<Task<string>> factoryAsync, DistributedCacheEntryOptions? options = null, bool cacheInBackground = false, CancellationToken cToken = default)
|
|
||||||
{
|
|
||||||
var value = await cache.GetStringAsync(key, cToken);
|
|
||||||
if(value is null)
|
|
||||||
{
|
|
||||||
// create new and save
|
|
||||||
value = await factoryAsync();
|
|
||||||
|
|
||||||
Task CacheAsync() => options is null
|
|
||||||
? cache.SetStringAsync(key: key, value: value, token: cToken)
|
|
||||||
: cache.SetStringAsync(key: key, value: value, options: options, token: cToken);
|
|
||||||
|
|
||||||
if (cacheInBackground)
|
|
||||||
_ = Task.Run(async () => await CacheAsync(), cToken);
|
|
||||||
else
|
|
||||||
await CacheAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region DateTime
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="factory"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <param name="cacheInBackground"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<DateTime> GetOrSetAsync(this IDistributedCache cache, string key, Func<DateTime> factory, DistributedCacheEntryOptions? options = null, bool cacheInBackground = false, CancellationToken cToken = default)
|
|
||||||
{
|
|
||||||
if (await cache.GetDateTimeAsync(key, cToken) is DateTime dateTimeValue)
|
|
||||||
return dateTimeValue;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// create new and save
|
|
||||||
var newValue = factory();
|
|
||||||
|
|
||||||
Task CacheAsync() => options is null
|
|
||||||
? cache.SetDateTimeAsync(key, newValue, cToken: cToken)
|
|
||||||
: cache.SetDateTimeAsync(key, newValue, options, cToken);
|
|
||||||
|
|
||||||
if (cacheInBackground)
|
|
||||||
_ = Task.Run(async () => await CacheAsync(), cToken);
|
|
||||||
else
|
|
||||||
await CacheAsync();
|
|
||||||
|
|
||||||
return newValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="factory"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <param name="cacheInBackground"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<DateTime> GetOrSetAsync(this IDistributedCache cache, string key, Func<Task<DateTime>> factory, DistributedCacheEntryOptions? options = null, bool cacheInBackground = false, CancellationToken cToken = default)
|
|
||||||
{
|
|
||||||
if (await cache.GetDateTimeAsync(key, cToken) is DateTime dateTimeValue)
|
|
||||||
return dateTimeValue;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// create new and save
|
|
||||||
var newValue = await factory();
|
|
||||||
|
|
||||||
Task CacheAsync() => options is null
|
|
||||||
? cache.SetDateTimeAsync(key, newValue, cToken: cToken)
|
|
||||||
: cache.SetDateTimeAsync(key, newValue, options, cToken);
|
|
||||||
|
|
||||||
if (cacheInBackground)
|
|
||||||
_ = Task.Run(async () => await CacheAsync(), cToken);
|
|
||||||
else
|
|
||||||
await CacheAsync();
|
|
||||||
|
|
||||||
return newValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class DecodingExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Validates whether a given string is a correctly formatted Base-64 encoded string.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This method checks the string for proper Base-64 formatting, which includes validating
|
|
||||||
/// the length of the string (must be divisible by 4). It also checks each character to ensure
|
|
||||||
/// it belongs to the Base-64 character set (A-Z, a-z, 0-9, '+', '/', and '=' for padding).
|
|
||||||
/// The method ensures that padding characters ('=') only appear at the end of the string and
|
|
||||||
/// are in a valid configuration (either one '=' at the end if the string's length % 4 is 3,
|
|
||||||
/// or two '==' if the length % 4 is 2).
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="input">The Base-64 encoded string to validate.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the string is a valid Base-64 encoded string; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
/// <example>
|
|
||||||
/// <code>
|
|
||||||
/// string testString = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnk=";
|
|
||||||
/// bool isValid = IsValidBase64String(testString);
|
|
||||||
/// Console.WriteLine(isValid); // Output: true
|
|
||||||
/// </code>
|
|
||||||
/// </example>
|
|
||||||
public static bool IsBase64String(this string input)
|
|
||||||
{
|
|
||||||
// Check if the string is null or empty
|
|
||||||
if (string.IsNullOrEmpty(input))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace valid base-64 padding
|
|
||||||
input = input.Trim();
|
|
||||||
int mod4 = input.Length % 4;
|
|
||||||
if (mod4 > 0)
|
|
||||||
{
|
|
||||||
// Base-64 string lengths should be divisible by 4
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check each character to ensure it is valid base-64
|
|
||||||
foreach (char c in input)
|
|
||||||
{
|
|
||||||
if (!char.IsLetterOrDigit(c) && c != '+' && c != '/' && c != '=')
|
|
||||||
{
|
|
||||||
// Invalid character detected
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure no invalid padding scenarios exist
|
|
||||||
if (input.EndsWith("==") && input.Length % 4 == 0 ||
|
|
||||||
input.EndsWith("=") && input.Length % 4 == 3)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return input.IndexOf('=') == -1; // No padding allowed except at the end
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="encodedKey"></param>
|
|
||||||
/// <param name="decodedKeys"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static bool TryDecode(this string encodedKey, out string[] decodedKeys)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
byte[] bytes = Convert.FromBase64String(encodedKey);
|
|
||||||
string decodedString = Encoding.UTF8.GetString(bytes);
|
|
||||||
decodedKeys = decodedString.Split(new string[] { "::" }, StringSplitOptions.None);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch(ArgumentNullException) { }
|
|
||||||
catch (FormatException) { }
|
|
||||||
catch(ArgumentException) { }
|
|
||||||
|
|
||||||
decodedKeys = Array.Empty<string>();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="decodedKeys"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static EncodeType GetEncodeType(this string[] decodedKeys) => decodedKeys.Length switch
|
|
||||||
{
|
|
||||||
2 => EncodeType.EnvelopeReceiver,
|
|
||||||
3 => long.TryParse(decodedKeys[1], out var _) ? EncodeType.EnvelopeReceiverReadOnly : EncodeType.Undefined,
|
|
||||||
_ => EncodeType.Undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="decodedKeys"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="InvalidOperationException"></exception>
|
|
||||||
public static (string? EnvelopeUuid, string? ReceiverSignature) ParseEnvelopeReceiverId(this string[] decodedKeys)
|
|
||||||
=> decodedKeys.GetEncodeType() == EncodeType.EnvelopeReceiver
|
|
||||||
? (EnvelopeUuid: decodedKeys[0], ReceiverSignature: decodedKeys[1])
|
|
||||||
: throw new InvalidOperationException("Attempted to convert a decoded other than type EnvelopeReceiver to EnvelopeReceiver.");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="decodedKeys"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="InvalidOperationException"></exception>
|
|
||||||
public static long ParseReadOnlyId(this string[] decodedKeys)
|
|
||||||
=> decodedKeys.GetEncodeType() == EncodeType.EnvelopeReceiverReadOnly
|
|
||||||
? long.Parse(decodedKeys[1])
|
|
||||||
: throw new InvalidOperationException("Attempted to convert a decoded other than type EnvelopeReceiver to EnvelopeReceiver. ");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Decodes the envelope receiver ID and extracts the envelope UUID and receiver signature.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiverId">The base64 encoded string containing the envelope UUID and receiver signature.</param>
|
|
||||||
/// <returns>A tuple containing the envelope UUID and receiver signature.</returns>
|
|
||||||
public static (string? EnvelopeUuid, string? ReceiverSignature) DecodeEnvelopeReceiverId(this string envelopeReceiverId)
|
|
||||||
{
|
|
||||||
if (!envelopeReceiverId.IsBase64String())
|
|
||||||
{
|
|
||||||
return (null, null);
|
|
||||||
}
|
|
||||||
byte[] bytes = Convert.FromBase64String(envelopeReceiverId);
|
|
||||||
string decodedString = Encoding.UTF8.GetString(bytes);
|
|
||||||
string[] parts = decodedString.Split(new string[] { "::" }, StringSplitOptions.None);
|
|
||||||
|
|
||||||
if (parts.Length > 1)
|
|
||||||
return (EnvelopeUuid: parts[0], ReceiverSignature: parts[1]);
|
|
||||||
else
|
|
||||||
return (string.Empty, string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiverReadOnlyId"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static long? DecodeEnvelopeReceiverReadOnlyId(this string envelopeReceiverReadOnlyId)
|
|
||||||
{
|
|
||||||
if (!envelopeReceiverReadOnlyId.IsBase64String())
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
byte[] bytes = Convert.FromBase64String(envelopeReceiverReadOnlyId);
|
|
||||||
string decodedString = Encoding.UTF8.GetString(bytes);
|
|
||||||
string[] parts = decodedString.Split(new string[] { "::" }, StringSplitOptions.None);
|
|
||||||
|
|
||||||
if (parts.Length > 2)
|
|
||||||
return long.TryParse(parts[1], out long readOnlyId) ? readOnlyId : null;
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the envelope UUID from the decoded envelope receiver ID.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiverId">The base64 encoded string to decode.</param>
|
|
||||||
/// <returns>The envelope UUID.</returns>
|
|
||||||
public static string? GetEnvelopeUuid(this string envelopeReceiverId) => envelopeReceiverId.DecodeEnvelopeReceiverId().EnvelopeUuid;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the receiver signature from the decoded envelope receiver ID.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiverId">The base64 encoded string to decode.</param>
|
|
||||||
/// <returns>The receiver signature.</returns>
|
|
||||||
public static string? GetReceiverSignature(this string envelopeReceiverId) => envelopeReceiverId.DecodeEnvelopeReceiverId().ReceiverSignature;
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides extension methods for decoding and extracting information from an envelope receiver ID.
|
|
||||||
/// </summary>
|
|
||||||
public static class EncodingExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="readOnlyId"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string ToEnvelopeKey(this long readOnlyId)
|
|
||||||
{
|
|
||||||
//The random number is used as a salt to increase security but it is not saved in the database.
|
|
||||||
string combinedString = $"{Random.Shared.Next()}::{readOnlyId}::{Random.Shared.Next()}";
|
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(combinedString);
|
|
||||||
string base64String = Convert.ToBase64String(bytes);
|
|
||||||
|
|
||||||
return base64String;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="input"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string ToEnvelopeKey(this (string envelopeUuid, string receiverSignature) input)
|
|
||||||
{
|
|
||||||
string combinedString = $"{input.envelopeUuid}::{input.receiverSignature}";
|
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(combinedString);
|
|
||||||
string base64String = Convert.ToBase64String(bytes);
|
|
||||||
|
|
||||||
return base64String;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class JsonExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string ToJson(this object obj, JsonSerializerOptions? options = null)
|
|
||||||
=> JsonSerializer.Serialize(obj, options);
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class LoggerExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger"></param>
|
|
||||||
/// <param name="envelopeReceiverId"></param>
|
|
||||||
/// <param name="exception"></param>
|
|
||||||
/// <param name="message"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
public static void LogEnvelopeError(this ILogger logger, string envelopeReceiverId, Exception? exception = null, string? message = null, params object?[] args)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder().AppendLine(envelopeReceiverId.DecodeEnvelopeReceiverId().ToTitle());
|
|
||||||
|
|
||||||
if (message is not null)
|
|
||||||
sb.AppendLine(message);
|
|
||||||
|
|
||||||
if (exception is null)
|
|
||||||
logger.Log(LogLevel.Error, sb.ToString(), args);
|
|
||||||
else
|
|
||||||
logger.Log(LogLevel.Error, exception, sb.AppendLine(exception.Message).ToString(), args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger"></param>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <param name="exception"></param>
|
|
||||||
/// <param name="message"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
public static void LogEnvelopeError(this ILogger logger, string? uuid, string? signature = null, Exception? exception = null, string? message = null, params object?[] args)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder($"Envelope Uuid: {uuid}");
|
|
||||||
|
|
||||||
if (signature is not null)
|
|
||||||
sb.AppendLine().Append($"Receiver Signature: {signature}");
|
|
||||||
|
|
||||||
if (message is not null)
|
|
||||||
sb.AppendLine().Append(message);
|
|
||||||
|
|
||||||
if (exception is null)
|
|
||||||
logger.Log(LogLevel.Error, sb.ToString(), args);
|
|
||||||
else
|
|
||||||
logger.Log(LogLevel.Error, exception, sb.ToString(), args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiverTuple"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string ToTitle(this (string? UUID, string? Signature) envelopeReceiverTuple)
|
|
||||||
{
|
|
||||||
return $"UUID is {envelopeReceiverTuple.UUID} and signature is {envelopeReceiverTuple.Signature}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto.Messaging;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides extension methods for common mapping and conversion operations.
|
|
||||||
/// </summary>
|
|
||||||
public static class MappingExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the response indicates a successful "OK" message status.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="gtxMessagingResponse">The response object to evaluate.</param>
|
|
||||||
/// <returns><see langword="true"/> if the response contains a "message-status" key with a value of "ok" (case-insensitive);
|
|
||||||
/// otherwise, <see langword="false"/>.</returns>
|
|
||||||
public static bool Ok(this GtxMessagingResponse gtxMessagingResponse)
|
|
||||||
=> gtxMessagingResponse.TryGetValue("message-status", out var status)
|
|
||||||
&& status?.ToString()?.ToLower() == "ok";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the specified byte array to its equivalent string representation encoded in base-64.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bytes">The byte array to encode.</param>
|
|
||||||
/// <returns>A base-64 encoded string representation of the input byte array.</returns>
|
|
||||||
public static string ToBase64String(this byte[] bytes)
|
|
||||||
=> Convert.ToBase64String(bytes);
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class MemoryCacheExtensions
|
|
||||||
{
|
|
||||||
private static readonly Guid BaseId = Guid.NewGuid();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEnum"></typeparam>
|
|
||||||
/// <param name="memoryCache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="ignores"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="InvalidOperationException"></exception>
|
|
||||||
public static IDictionary<string, int> GetEnumAsDictionary<TEnum>(this IMemoryCache memoryCache, string key = "", params object[] ignores)
|
|
||||||
where TEnum : Enum
|
|
||||||
=> memoryCache.GetOrCreate(BaseId + typeof(TEnum).FullName + key, _ =>
|
|
||||||
{
|
|
||||||
var mergedIgnores = new List<TEnum>();
|
|
||||||
|
|
||||||
foreach (var ignore in ignores)
|
|
||||||
{
|
|
||||||
if (ignore is IEnumerable<TEnum> ignoreList)
|
|
||||||
mergedIgnores.AddRange(ignoreList);
|
|
||||||
else if (ignore is TEnum ignoreVal)
|
|
||||||
mergedIgnores.Add(ignoreVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Enum.GetValues(typeof(TEnum))
|
|
||||||
.Cast<TEnum>()
|
|
||||||
.Where(e => !mergedIgnores.Contains(e))
|
|
||||||
.ToDictionary(e => e.ToString(), e => Convert.ToInt32(e));
|
|
||||||
})
|
|
||||||
?? throw new InvalidOperationException($"Failed to cache or retrieve enum dictionary for type '{typeof(TEnum).FullName}'.");
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using EnvelopeGenerator.Application.Common.Query;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using EnvelopeGenerator.Domain.Interfaces;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class QueryExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
/// <param name="root"></param>
|
|
||||||
/// <param name="query"></param>
|
|
||||||
/// <param name="notnull"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="BadRequestException"></exception>
|
|
||||||
public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> root, EnvelopeQueryBase query, bool notnull = true)
|
|
||||||
where TEntity : IHasEnvelope
|
|
||||||
{
|
|
||||||
if (query.Id is not null)
|
|
||||||
root = root.Where(e => e.Envelope!.Id == query.Id);
|
|
||||||
else if (query.Uuid is not null)
|
|
||||||
root = root.Where(e => e.Envelope!.Uuid == query.Uuid);
|
|
||||||
else if (notnull)
|
|
||||||
throw new BadRequestException(
|
|
||||||
"Either Envelope Id or Envelope Uuid must be provided in the query."
|
|
||||||
);
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="root"></param>
|
|
||||||
/// <param name="query"></param>
|
|
||||||
/// <param name="notnull"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="BadRequestException"></exception>
|
|
||||||
public static IQueryable<Envelope> Where(this IQueryable<Envelope> root, EnvelopeQueryBase query, bool notnull = true)
|
|
||||||
{
|
|
||||||
if (query.Id is not null)
|
|
||||||
root = root.Where(e => e.Id == query.Id);
|
|
||||||
else if (query.Uuid is not null)
|
|
||||||
root = root.Where(e => e.Uuid == query.Uuid);
|
|
||||||
else if (notnull)
|
|
||||||
throw new BadRequestException(
|
|
||||||
"Either Envelope Id or Envelope Uuid must be provided in the query."
|
|
||||||
);
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
/// <param name="root"></param>
|
|
||||||
/// <param name="query"></param>
|
|
||||||
/// <param name="notnull"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="BadRequestException"></exception>
|
|
||||||
public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> root, ReceiverQueryBase query, bool notnull = true)
|
|
||||||
where TEntity : IHasReceiver
|
|
||||||
{
|
|
||||||
if (query.Id is not null)
|
|
||||||
root = root.Where(e => e.Receiver!.Id == query.Id);
|
|
||||||
else if (query.EmailAddress is not null)
|
|
||||||
root = root.Where(e => e.Receiver!.EmailAddress == query.EmailAddress);
|
|
||||||
else if (query.Signature is not null)
|
|
||||||
root = root.Where(e => e.Receiver!.Signature == query.Signature);
|
|
||||||
else if (notnull)
|
|
||||||
throw new BadRequestException(
|
|
||||||
"Receiver must have at least one identifier (Id, EmailAddress, or Signature)."
|
|
||||||
);
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="root"></param>
|
|
||||||
/// <param name="query"></param>
|
|
||||||
/// <param name="notnull"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="BadRequestException"></exception>
|
|
||||||
public static IQueryable<Receiver> Where(this IQueryable<Receiver> root, ReceiverQueryBase query, bool notnull = true)
|
|
||||||
{
|
|
||||||
if (query.Id is not null)
|
|
||||||
root = root.Where(e => e.Id == query.Id);
|
|
||||||
else if (query.EmailAddress is not null)
|
|
||||||
root = root.Where(e => e.EmailAddress == query.EmailAddress);
|
|
||||||
else if (query.Signature is not null)
|
|
||||||
root = root.Where(e => e.Signature == query.Signature);
|
|
||||||
else if (notnull)
|
|
||||||
throw new BadRequestException(
|
|
||||||
"Receiver must have at least one identifier (Id, EmailAddress, or Signature)."
|
|
||||||
);
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
/// <typeparam name="TEnvelopeQuery"></typeparam>
|
|
||||||
/// <typeparam name="TReceiverQuery"></typeparam>
|
|
||||||
/// <param name="root"></param>
|
|
||||||
/// <param name="query"></param>
|
|
||||||
/// <param name="notnull"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static IQueryable<TEntity> Where<TEntity, TEnvelopeQuery, TReceiverQuery>(this IQueryable<TEntity> root, EnvelopeReceiverQueryBase<TEnvelopeQuery, TReceiverQuery> query, bool notnull = true)
|
|
||||||
where TEntity : IHasEnvelope, IHasReceiver
|
|
||||||
where TEnvelopeQuery : EnvelopeQueryBase, new()
|
|
||||||
where TReceiverQuery : ReceiverQueryBase, new()
|
|
||||||
=> root.Where(query.Envelope, notnull).Where(query.Receiver, notnull);
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using OtpNet;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class StringExtension
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="totp"></param>
|
|
||||||
/// <param name="secret"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static bool IsValidTotp(this string totp, string secret)
|
|
||||||
{
|
|
||||||
var secret_bytes = Base32Encoding.ToBytes(secret);
|
|
||||||
var secret_totp = new Totp(secret_bytes);
|
|
||||||
return secret_totp.VerifyTotp(totp, out _, VerificationWindow.RfcSpecifiedNetworkDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="seperator"></param>
|
|
||||||
/// <param name="values"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string Join(this IEnumerable<string> values, string seperator) => string.Join(seperator, values);
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
using DigitalData.Core.Exceptions;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Extension methods for tasks
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static class TaskExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Awaits the specified task and ensures that the result is not <c>null</c>.
|
|
||||||
/// If the result is <c>null</c>, the exception created by factory-method is thrown.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the result.</typeparam>
|
|
||||||
/// <typeparam name="TException">The type of the exception.</typeparam>
|
|
||||||
/// <param name="task">The task to await.</param>
|
|
||||||
/// <param name="factory">Exception provider</param>
|
|
||||||
/// <returns>The awaited result if not <c>null</c>.</returns>
|
|
||||||
/// <exception>Thrown if the result is <c>null</c>.</exception>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static async Task<T> ThrowIfNull<T, TException>(this Task<T?> task, Func<TException> factory) where TException : Exception
|
|
||||||
{
|
|
||||||
var result = await task;
|
|
||||||
return result ?? throw factory();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Awaits the specified task and ensures that the result is not <c>empty</c>.
|
|
||||||
/// If the result contains no elements, the exception created by factory-method is thrown.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The element type of the collection.</typeparam>
|
|
||||||
/// <typeparam name="TException">The type of the exception.</typeparam>
|
|
||||||
/// <param name="task">The task to await.</param>
|
|
||||||
/// <param name="factory">Exception provider</param>
|
|
||||||
/// <returns>The awaited collection if it is not <c>null</c> or empty.</returns>
|
|
||||||
/// <exception cref="NotFoundException">Thrown if the result is <c>null</c> or empty.</exception>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static async Task<IEnumerable<T>> ThrowIfEmpty<T, TException>(this Task<IEnumerable<T>> task, Func<TException> factory) where TException : Exception
|
|
||||||
{
|
|
||||||
var result = await task;
|
|
||||||
return result?.Any() ?? false ? result : throw factory();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <typeparam name="I"></typeparam>
|
|
||||||
/// <param name="task"></param>
|
|
||||||
/// <param name="act"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static async Task<I> Then<T, I>(this Task<T> task, Func<T, I> act)
|
|
||||||
{
|
|
||||||
var res = await task;
|
|
||||||
return act(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="task"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static Task<T?> FirstOrDefaultAsync<T>(this Task<IEnumerable<T>> task) => task.Then(t => t.FirstOrDefault());
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <typeparam name="TException"></typeparam>
|
|
||||||
/// <param name="task"></param>
|
|
||||||
/// <param name="factory"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Task<T> FirstAsync<T, TException>(this Task<IEnumerable<T>> task, Func<TException> factory)
|
|
||||||
where TException : Exception
|
|
||||||
=> task.Then(t => t.FirstOrDefault() ?? throw factory());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class Exceptions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static NotFoundException NotFound() => new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static BadRequestException BadRequest() => new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static ForbiddenException Forbidden() => new();
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using Ganss.Xss;
|
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using System.Text.Encodings.Web;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class XSSExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="encoder"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string? TryEncode(this string? value, UrlEncoder encoder) => value is null ? value : encoder.Encode(value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="encoder"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string? TryEncode(this LocalizedString? value, UrlEncoder encoder) => value is null ? null : encoder.Encode(value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="html"></param>
|
|
||||||
/// <param name="sanitizer"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string? TrySanitize(this string? html, HtmlSanitizer sanitizer) => html is null ? html : sanitizer.Sanitize(html);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="html"></param>
|
|
||||||
/// <param name="sanitizer"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string? TrySanitize(this LocalizedString? html, HtmlSanitizer sanitizer) => html is null ? null : sanitizer.Sanitize(html);
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IConfigRepository : ICRUDRepository<Config, int>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<Config?> ReadFirstAsync();
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IDocumentReceiverElementRepository : ICRUDRepository<Signature, int>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IDocumentStatusRepository : ICRUDRepository<DocumentStatus, int>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IEmailTemplateRepository : ICRUDRepository<EmailTemplate, int>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<EmailTemplate?> ReadByNameAsync(EmailTemplateType type);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IEnvelopeDocumentRepository : ICRUDRepository<Document, int>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IEnvelopeHistoryRepository : ICRUDRepository<History, long>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="userReference"></param>
|
|
||||||
/// <param name="status"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<int> CountAsync(int? envelopeId = null, string? userReference = null, EnvelopeStatus? status = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="userReference"></param>
|
|
||||||
/// <param name="status"></param>
|
|
||||||
/// <param name="withSender"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<History>> ReadAsync(int? envelopeId = null, string? userReference = null, EnvelopeStatus? status = null, bool withSender = false, bool withReceiver = false);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IEnvelopeReceiverReadOnlyRepository : ICRUDRepository<EnvelopeReceiverReadOnly, long>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Application.Envelopes.Queries;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IEnvelopeReceiverRepository : ICRUDRepository<EnvelopeReceiver, (int Envelope, int Receiver)>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="withEnvelope"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<EnvelopeReceiver>> ReadByUuidAsync(string uuid, bool withEnvelope = true, bool withReceiver = false, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <param name="withEnvelope"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<EnvelopeReceiver>> ReadBySignatureAsync(string signature, bool withEnvelope = false, bool withReceiver = true, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <param name="withEnvelope"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<EnvelopeReceiver?> ReadByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<string?> ReadAccessCodeAsync(string uuid, string signature, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<int> CountAsync(string uuid, string signature);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="receiverId"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<EnvelopeReceiver?> ReadByIdAsync(int envelopeId, int receiverId, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="receiverId"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<string?> ReadAccessCodeByIdAsync(int envelopeId, int receiverId, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="username"></param>
|
|
||||||
/// <param name="min_status"></param>
|
|
||||||
/// <param name="max_status"></param>
|
|
||||||
/// <param name="ignore_statuses"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<EnvelopeReceiver>> ReadByUsernameAsync(string username, EnvelopeStatus? min_status = null, EnvelopeStatus? max_status = null, params EnvelopeStatus[] ignore_statuses);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="email"></param>
|
|
||||||
/// <param name="id"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<EnvelopeReceiver?> ReadLastByReceiverAsync(string? email = null, int? id = null, string? signature = null);
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IEnvelopeRepository : ICRUDRepository<Envelope, int>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="documents"></param>
|
|
||||||
/// <param name="history"></param>
|
|
||||||
/// <param name="documentReceiverElement"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<Envelope>> ReadAllWithAsync(bool documents = false, bool history = false, bool documentReceiverElement = false);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="withDocuments"></param>
|
|
||||||
/// <param name="withHistory"></param>
|
|
||||||
/// <param name="withDocumentReceiverElement"></param>
|
|
||||||
/// <param name="withUser"></param>
|
|
||||||
/// <param name="withAll"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<Envelope?> ReadByUuidAsync(string uuid, bool withDocuments = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withUser = false, bool withAll = false);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userId"></param>
|
|
||||||
/// <param name="min_status"></param>
|
|
||||||
/// <param name="max_status"></param>
|
|
||||||
/// <param name="ignore_statuses"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<Envelope>> ReadByUserAsync(int userId, EnvelopeStatus? min_status = null, EnvelopeStatus? max_status = null, params EnvelopeStatus[] ignore_statuses);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IEnvelopeTypeRepository : ICRUDRepository<EnvelopeType, int>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IReceiverRepository : ICRUDRepository<Receiver, int>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="emailAddress"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<Receiver?> ReadByAsync(string? emailAddress = null, string? signature = null);
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public interface IDocumentExecutor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="base64"></param>
|
|
||||||
/// <param name="envelope_uuid"></param>
|
|
||||||
/// <param name="cancellation"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<Document> CreateDocumentAsync(string base64, string envelope_uuid, CancellationToken cancellation = default);
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public interface IEnvelopeExecutor : ISQLExecutor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userId"></param>
|
|
||||||
/// <param name="title"></param>
|
|
||||||
/// <param name="message"></param>
|
|
||||||
/// <param name="tfaEnabled"></param>
|
|
||||||
/// <param name="cancellation"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<Envelope> CreateEnvelopeAsync(int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default);
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public interface IEnvelopeReceiverExecutor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelope_uuid"></param>
|
|
||||||
/// <param name="emailAddress"></param>
|
|
||||||
/// <param name="salutation"></param>
|
|
||||||
/// <param name="phone"></param>
|
|
||||||
/// <param name="cancellation"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<EnvelopeReceiver?> AddEnvelopeReceiverAsync(string envelope_uuid, string emailAddress, string? salutation = null, string? phone = null, CancellationToken cancellation = default);
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides methods for executing common queries on a given entity type.
|
|
||||||
/// This interface abstracts away the direct usage of ORM libraries (such as Entity Framework) for querying data
|
|
||||||
/// and provides asynchronous and synchronous operations for querying a collection or single entity.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity">The type of the entity being queried.</typeparam>
|
|
||||||
public interface IQuery<TEntity>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Asynchronously retrieves the first entity or a default value if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A task that represents the asynchronous operation. The task result contains the entity or a default value.</returns>
|
|
||||||
public Task<TEntity?> FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Asynchronously retrieves a single entity or a default value if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A task that represents the asynchronous operation. The task result contains the entity or a default value.</returns>
|
|
||||||
public Task<TEntity?> SingleOrDefaultAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Asynchronously retrieves a list of entities.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A task that represents the asynchronous operation. The task result contains the list of entities.</returns>
|
|
||||||
public Task<IEnumerable<TEntity>> ToListAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Asynchronously retrieves the first entity. Throws an exception if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A task that represents the asynchronous operation. The task result contains the first entity.</returns>
|
|
||||||
public Task<TEntity> FirstAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Asynchronously retrieves a single entity. Throws an exception if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A task that represents the asynchronous operation. The task result contains the single entity.</returns>
|
|
||||||
public Task<TEntity> SingleAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Synchronously retrieves the first entity or a default value if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The first entity or a default value.</returns>
|
|
||||||
public TEntity? FirstOrDefault();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Synchronously retrieves a single entity or a default value if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The single entity or a default value.</returns>
|
|
||||||
public TEntity? SingleOrDefault();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Synchronously retrieves a list of entities.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The list of entities.</returns>
|
|
||||||
public IEnumerable<TEntity> ToList();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Synchronously retrieves the first entity. Throws an exception if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The first entity.</returns>
|
|
||||||
public TEntity First();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Synchronously retrieves a single entity. Throws an exception if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The single entity.</returns>
|
|
||||||
public TEntity Single();
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a raw SQL query contract.
|
|
||||||
/// </summary>
|
|
||||||
public interface ISQL
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the raw SQL query string.
|
|
||||||
/// </summary>
|
|
||||||
string Raw { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a typed SQL query contract for a specific entity.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity">The type of the entity associated with the SQL query.</typeparam>
|
|
||||||
public interface ISQL<TEntity> : ISQL
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines methods for executing raw SQL queries or custom SQL query classes and returning query executors for further operations.
|
|
||||||
/// Provides abstraction for raw SQL execution as well as mapping the results to <typeparamref name="TEntity"/> objects.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity">The entity type to which the SQL query results will be mapped.</typeparam>
|
|
||||||
public interface ISQLExecutor<TEntity>: ISQLExecutor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Executes a raw SQL query and returns an <see cref="IQuery{TEntity}"/> for further querying operations on the result.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sql">The raw SQL query to execute.</param>
|
|
||||||
/// <param name="cancellation">Optional cancellation token for the operation.</param>
|
|
||||||
/// <param name="parameters">Optional parameters for the SQL query.</param>
|
|
||||||
/// <returns>An <see cref="IQuery{TEntity}"/> instance for further query operations on the result.</returns>
|
|
||||||
IQuery<TEntity> Execute(string sql, CancellationToken cancellation = default, params object[] parameters);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Executes a custom SQL query defined by a class that implements <see cref="ISQL{TEntity}"/> and returns an <see cref="IQuery{TEntity}"/> for further querying operations on the result.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TSQL">The type of the custom SQL query class implementing <see cref="ISQL{TEntity}"/>.</typeparam>
|
|
||||||
/// <param name="cancellation">Optional cancellation token for the operation.</param>
|
|
||||||
/// <param name="parameters">Optional parameters for the SQL query.</param>
|
|
||||||
/// <returns>An <see cref="IQuery{TEntity}"/> instance for further query operations on the result.</returns>
|
|
||||||
IQuery<TEntity> Execute<TSQL>(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL<TEntity>;
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public interface ISQLExecutor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Executes a raw SQL query and returns an <see cref="IQuery{TEntity}"/> for further querying operations on the result.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity">The entity type to which the SQL query results will be mapped.</typeparam>
|
|
||||||
/// <param name="sql">The raw SQL query to execute.</param>
|
|
||||||
/// <param name="parameters">Parameters for the SQL query.</param>
|
|
||||||
/// <param name="cancellation">Optional cancellation token for the operation.</param>
|
|
||||||
/// <returns>An <see cref="IQuery{TEntity}"/> instance for further query operations on the result.</returns>
|
|
||||||
Task<IEnumerable<TEntity>> Execute<TEntity>(string sql, DynamicParameters parameters, CancellationToken cancellation = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Executes a custom SQL query defined by a class that implements <see cref="ISQL{TEntity}"/> and returns an <see cref="IQuery{TEntity}"/> for further querying operations on the result.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity">The entity type to which the SQL query results will be mapped.</typeparam>
|
|
||||||
/// <typeparam name="TSQL">The type of the custom SQL query class implementing <see cref="ISQL{TEntity}"/>.</typeparam>
|
|
||||||
/// <param name="parameters">Parameters for the SQL query.</param>
|
|
||||||
/// <param name="cancellation">Optional cancellation token for the operation.</param>
|
|
||||||
/// <returns>An <see cref="IQuery{TEntity}"/> instance for further query operations on the result.</returns>
|
|
||||||
Task<IEnumerable<TEntity>> Execute<TEntity, TSQL>(DynamicParameters parameters, CancellationToken cancellation = default) where TSQL : ISQL;
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
using OtpNet;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public interface IAuthenticator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
string GenerateCode(int length);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
string GenerateTotpSecretKey(int? length = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userEmail"></param>
|
|
||||||
/// <param name="secretKey"></param>
|
|
||||||
/// <param name="issuer"></param>
|
|
||||||
/// <param name="totpUrlFormat"></param>
|
|
||||||
/// <param name="pixelsPerModule"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
byte[] GenerateTotpQrCode(string userEmail, string secretKey, string? issuer = null, string? totpUrlFormat = null, int? pixelsPerModule = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userEmail"></param>
|
|
||||||
/// <param name="length"></param>
|
|
||||||
/// <param name="issuer"></param>
|
|
||||||
/// <param name="totpUrlFormat"></param>
|
|
||||||
/// <param name="pixelsPerModule"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
byte[] GenerateTotpQrCode(string userEmail, int? length = null, string? issuer = null, string? totpUrlFormat = null, int? pixelsPerModule = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="secretKey"></param>
|
|
||||||
/// <param name="step"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
string GenerateTotp(string secretKey, int step = 30);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="totpCode"></param>
|
|
||||||
/// <param name="secretKey"></param>
|
|
||||||
/// <param name="step"></param>
|
|
||||||
/// <param name="window"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
bool VerifyTotp(string totpCode, string secretKey, int step = 30, VerificationWindow? window = null);
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application;
|
|
||||||
using DigitalData.Core.Abstraction.Application.DTO;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public interface IConfigService : IReadService<ConfigDto, Config, int>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<ConfigDto>> ReadFirstAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<ConfigDto> ReadDefaultAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<string> ReadDefaultSignatureHost();
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user