Compare commits
34 Commits
master
...
refactor/a
| Author | SHA1 | Date | |
|---|---|---|---|
| 2fcea78574 | |||
| e8e428f935 | |||
| 9450ed3486 | |||
| 583a07c646 | |||
| 51ad4fbc2c | |||
| 50ac7570ea | |||
| 5465996563 | |||
| 1b840f4ae3 | |||
| 3923a3b403 | |||
| ada621ac46 | |||
| abbe6a26a9 | |||
| 3066dac541 | |||
| b1aa6d6639 | |||
| 31fe1c34f2 | |||
| d7644bfe07 | |||
| 4759b16a85 | |||
| cfdfb43631 | |||
| 6254bb6e3f | |||
| f995fa9fc3 | |||
| c2fefe798d | |||
| 849a282ec5 | |||
| 6b23dcdba7 | |||
| a60d0f63e2 | |||
| 2481059b49 | |||
| 6334097d5e | |||
| 9baa126c8c | |||
| 1ef46013cd | |||
| 72dffd1043 | |||
| eda30472b9 | |||
| 75846573da | |||
| f59c0d90ad | |||
| cf40449112 | |||
| a59d4836d4 | |||
| f475cf4ea9 |
@@ -6,19 +6,19 @@ using EnvelopeGenerator.Application.Common.Notifications.DocSigned;
|
|||||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
||||||
using EnvelopeGenerator.Application.Histories.Queries;
|
using EnvelopeGenerator.Application.Histories.Queries;
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
using EnvelopeGenerator.GeneratorAPI.Extensions;
|
using EnvelopeGenerator.API.Extensions;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
namespace EnvelopeGenerator.API.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Manages annotations and signature lifecycle for envelopes.
|
/// Manages annotations and signature lifecycle for envelopes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Authorize(Roles = ReceiverRole.FullyAuth)]
|
[Authorize(Roles = Role.Receiver.FullyAuth)]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class AnnotationController : ControllerBase
|
public class AnnotationController : ControllerBase
|
||||||
@@ -54,19 +54,13 @@ public class AnnotationController : ControllerBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="psPdfKitAnnotation">Annotation payload.</param>
|
/// <param name="psPdfKitAnnotation">Annotation payload.</param>
|
||||||
/// <param name="cancel">Cancellation token.</param>
|
/// <param name="cancel">Cancellation token.</param>
|
||||||
[Authorize(Roles = ReceiverRole.FullyAuth)]
|
[Authorize(Roles = Role.Receiver.FullyAuth)]
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Obsolete("This endpoint is for PSPDF Kit.")]
|
[Obsolete("PSPDF Kit will no longer be used.")]
|
||||||
public async Task<IActionResult> CreateOrUpdate([FromBody] PsPdfKitAnnotation? psPdfKitAnnotation = null, CancellationToken cancel = default)
|
public async Task<IActionResult> CreateOrUpdate([FromBody] PsPdfKitAnnotation? psPdfKitAnnotation = null, CancellationToken cancel = default)
|
||||||
{
|
{
|
||||||
var signature = User.GetAuthReceiverSignature();
|
var signature = User.GetReceiverSignatureOfReceiver();
|
||||||
var uuid = User.GetAuthEnvelopeUuid();
|
var uuid = User.GetEnvelopeUuidOfReceiver();
|
||||||
|
|
||||||
if (signature is null || uuid is null)
|
|
||||||
{
|
|
||||||
_logger.LogError("Authorization failed: authenticated user does not have a valid signature or envelope UUID.");
|
|
||||||
return Unauthorized("User authentication is incomplete. Missing required claims for processing this request.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var envelopeReceiver = await _mediator.ReadEnvelopeReceiverAsync(uuid, signature, cancel).ThrowIfNull(Exceptions.NotFound);
|
var envelopeReceiver = await _mediator.ReadEnvelopeReceiverAsync(uuid, signature, cancel).ThrowIfNull(Exceptions.NotFound);
|
||||||
|
|
||||||
@@ -93,20 +87,14 @@ public class AnnotationController : ControllerBase
|
|||||||
/// Rejects the document for the current receiver.
|
/// Rejects the document for the current receiver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="reason">Optional rejection reason.</param>
|
/// <param name="reason">Optional rejection reason.</param>
|
||||||
[Authorize(Roles = ReceiverRole.FullyAuth)]
|
[Authorize(Roles = Role.Receiver.FullyAuth)]
|
||||||
[HttpPost("reject")]
|
[HttpPost("reject")]
|
||||||
[Obsolete("Use MediatR")]
|
[Obsolete("Use MediatR")]
|
||||||
public async Task<IActionResult> Reject([FromBody] string? reason = null)
|
public async Task<IActionResult> Reject([FromBody] string? reason = null)
|
||||||
{
|
{
|
||||||
var signature = User.GetAuthReceiverSignature();
|
var signature = User.GetReceiverSignatureOfReceiver();
|
||||||
var uuid = User.GetAuthEnvelopeUuid();
|
var uuid = User.GetEnvelopeUuidOfReceiver();
|
||||||
var mail = User.GetAuthReceiverMail();
|
var mail = User.GetReceiverMailOfReceiver();
|
||||||
if (uuid is null || signature is null || mail is null)
|
|
||||||
{
|
|
||||||
_logger.LogEnvelopeError(uuid: uuid, signature: signature,
|
|
||||||
message: @$"Unauthorized POST request in api\\envelope\\reject. One of claims, Envelope, signature or mail ({mail}) is null.");
|
|
||||||
return Unauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
var envRcvRes = await _envelopeReceiverService.ReadByUuidSignatureAsync(uuid: uuid, signature: signature);
|
var envRcvRes = await _envelopeReceiverService.ReadByUuidSignatureAsync(uuid: uuid, signature: signature);
|
||||||
|
|
||||||
59
EnvelopeGenerator.API/Controllers/AuthController.cs
Normal file
59
EnvelopeGenerator.API/Controllers/AuthController.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
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,9 +1,9 @@
|
|||||||
using EnvelopeGenerator.GeneratorAPI.Models.PsPdfKitAnnotation;
|
using EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
namespace EnvelopeGenerator.API.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Exposes configuration data required by the client applications.
|
/// Exposes configuration data required by the client applications.
|
||||||
@@ -22,6 +22,7 @@ public class ConfigController(IOptionsMonitor<AnnotationParams> annotationParams
|
|||||||
/// Returns annotation configuration that was previously rendered by MVC.
|
/// Returns annotation configuration that was previously rendered by MVC.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpGet("Annotations")]
|
[HttpGet("Annotations")]
|
||||||
|
[Obsolete("PSPDF Kit will no longer be used.")]
|
||||||
public IActionResult GetAnnotationParams()
|
public IActionResult GetAnnotationParams()
|
||||||
{
|
{
|
||||||
return Ok(_annotationParams.AnnotationJSObject);
|
return Ok(_annotationParams.AnnotationJSObject);
|
||||||
57
EnvelopeGenerator.API/Controllers/DocumentController.cs
Normal file
57
EnvelopeGenerator.API/Controllers/DocumentController.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ using DigitalData.Core.Abstraction.Application.Repository;
|
|||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
namespace EnvelopeGenerator.API.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Controller for managing temp templates.
|
/// Controller for managing temp templates.
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
using EnvelopeGenerator.Application.Envelopes.Commands;
|
using EnvelopeGenerator.API.Extensions;
|
||||||
|
using EnvelopeGenerator.Application.Envelopes.Commands;
|
||||||
using EnvelopeGenerator.Application.Envelopes.Queries;
|
using EnvelopeGenerator.Application.Envelopes.Queries;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
namespace EnvelopeGenerator.API.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Dieser Controller stellt Endpunkte für die Verwaltung von Umschlägen bereit.
|
/// Dieser Controller stellt Endpunkte für die Verwaltung von Umschlägen bereit.
|
||||||
@@ -3,7 +3,7 @@ using EnvelopeGenerator.Application.EnvelopeReceivers.Commands;
|
|||||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
||||||
using EnvelopeGenerator.Application.Envelopes.Queries;
|
using EnvelopeGenerator.Application.Envelopes.Queries;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
using EnvelopeGenerator.GeneratorAPI.Models;
|
using EnvelopeGenerator.API.Models;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@@ -13,8 +13,9 @@ using System.Data;
|
|||||||
using EnvelopeGenerator.Application.Common.SQL;
|
using EnvelopeGenerator.Application.Common.SQL;
|
||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
||||||
|
using EnvelopeGenerator.API.Extensions;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
namespace EnvelopeGenerator.API.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Controller für die Verwaltung von Umschlagempfängern.
|
/// Controller für die Verwaltung von Umschlagempfängern.
|
||||||
@@ -65,16 +66,7 @@ public class EnvelopeReceiverController : ControllerBase
|
|||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> GetEnvelopeReceiver([FromQuery] ReadEnvelopeReceiverQuery envelopeReceiver)
|
public async Task<IActionResult> GetEnvelopeReceiver([FromQuery] ReadEnvelopeReceiverQuery envelopeReceiver)
|
||||||
{
|
{
|
||||||
var username = User.GetUsernameOrDefault();
|
envelopeReceiver = envelopeReceiver with { Username = User.GetUsername() };
|
||||||
|
|
||||||
if (username is null)
|
|
||||||
{
|
|
||||||
_logger.LogError(@"Envelope Receiver dto cannot be sent because username claim is null. Potential authentication and authorization error. The value of other claims are [id: {id}], [username: {username}], [name: {name}], [prename: {prename}], [email: {email}].",
|
|
||||||
User.GetId(), User.GetUsernameOrDefault(), User.GetNameOrDefault(), User.GetPrenameOrDefault(), User.GetEmailOrDefault());
|
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
|
||||||
}
|
|
||||||
|
|
||||||
envelopeReceiver = envelopeReceiver with { Username = username };
|
|
||||||
|
|
||||||
var result = await _mediator.Send(envelopeReceiver);
|
var result = await _mediator.Send(envelopeReceiver);
|
||||||
|
|
||||||
@@ -225,18 +217,14 @@ public class EnvelopeReceiverController : ControllerBase
|
|||||||
using (SqlConnection conn = new(_cnnStr))
|
using (SqlConnection conn = new(_cnnStr))
|
||||||
{
|
{
|
||||||
conn.Open();
|
conn.Open();
|
||||||
var formattedSQL_hist = string.Format(sql_hist, envelope.Uuid.ToSqlParam(), 1003.ToSqlParam(), userId.ToSqlParam());
|
var formattedSQL_hist = string.Format(sql_hist, envelope.Uuid.ToSqlParam(), 1003.ToSqlParam(), User.GetId().ToSqlParam());
|
||||||
using (SqlCommand cmd = new SqlCommand(formattedSQL_hist, conn))
|
using SqlCommand cmd = new(formattedSQL_hist, conn);
|
||||||
{
|
cmd.CommandType = CommandType.Text;
|
||||||
cmd.CommandType = CommandType.Text;
|
|
||||||
|
|
||||||
using (SqlDataReader reader = cmd.ExecuteReader())
|
using SqlDataReader reader = cmd.ExecuteReader();
|
||||||
{
|
if (reader.Read())
|
||||||
if (reader.Read())
|
{
|
||||||
{
|
bool outSuccess = reader.GetBoolean(0);
|
||||||
bool outSuccess = reader.GetBoolean(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@@ -6,7 +6,7 @@ using EnvelopeGenerator.Application.Histories.Queries;
|
|||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
namespace EnvelopeGenerator.API.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Dieser Controller stellt Endpunkte für den Zugriff auf die Umschlaghistorie bereit.
|
/// Dieser Controller stellt Endpunkte für den Zugriff auf die Umschlaghistorie bereit.
|
||||||
@@ -113,6 +113,6 @@ public class HistoryController : ControllerBase
|
|||||||
public async Task<IActionResult> GetAllAsync([FromQuery] ReadHistoryQuery historyQuery, CancellationToken cancel)
|
public async Task<IActionResult> GetAllAsync([FromQuery] ReadHistoryQuery historyQuery, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
var history = await _mediator.Send(historyQuery, cancel).ThrowIfEmpty(Exceptions.NotFound);
|
var history = await _mediator.Send(historyQuery, cancel).ThrowIfEmpty(Exceptions.NotFound);
|
||||||
return Ok((historyQuery.OnlyLast ?? false) ? history.MaxBy(h => h.AddedWhen) : history);
|
return Ok((historyQuery.OnlyLast) ? history.MaxBy(h => h.AddedWhen) : history);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
using DigitalData.Core.API;
|
using DigitalData.Core.API;
|
||||||
using EnvelopeGenerator.Application.Resources;
|
using EnvelopeGenerator.Application.Resources;
|
||||||
using EnvelopeGenerator.CommonServices;
|
|
||||||
using Microsoft.AspNetCore.Localization;
|
using Microsoft.AspNetCore.Localization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
using EnvelopeGenerator.Application.Resources;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
namespace EnvelopeGenerator.API.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Controller für die Verwaltung der Lokalisierung und Spracheinstellungen.
|
/// Controller für die Verwaltung der Lokalisierung und Spracheinstellungen.
|
||||||
@@ -19,7 +19,7 @@ public class LocalizationController : ControllerBase
|
|||||||
private static readonly Guid L_KEY = Guid.NewGuid();
|
private static readonly Guid L_KEY = Guid.NewGuid();
|
||||||
|
|
||||||
private readonly ILogger<LocalizationController> _logger;
|
private readonly ILogger<LocalizationController> _logger;
|
||||||
private readonly IStringLocalizer<Model> _mLocalizer;
|
private readonly IStringLocalizer<Resource> _mLocalizer;
|
||||||
private readonly IStringLocalizer<Resource> _localizer;
|
private readonly IStringLocalizer<Resource> _localizer;
|
||||||
private readonly IMemoryCache _cache;
|
private readonly IMemoryCache _cache;
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ public class LocalizationController : ControllerBase
|
|||||||
ILogger<LocalizationController> logger,
|
ILogger<LocalizationController> logger,
|
||||||
IStringLocalizer<Resource> localizer,
|
IStringLocalizer<Resource> localizer,
|
||||||
IMemoryCache memoryCache,
|
IMemoryCache memoryCache,
|
||||||
IStringLocalizer<Model> _modelLocalizer)
|
IStringLocalizer<Resource> _modelLocalizer)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_localizer = localizer;
|
_localizer = localizer;
|
||||||
@@ -2,12 +2,12 @@ using DigitalData.Core.Abstraction.Application.DTO;
|
|||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
using EnvelopeGenerator.GeneratorAPI.Extensions;
|
using EnvelopeGenerator.API.Extensions;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
namespace EnvelopeGenerator.API.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Manages read-only envelope sharing flows.
|
/// Manages read-only envelope sharing flows.
|
||||||
@@ -37,22 +37,17 @@ public class ReadOnlyController : ControllerBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="createDto">Creation payload.</param>
|
/// <param name="createDto">Creation payload.</param>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Authorize(Roles = ReceiverRole.FullyAuth)]
|
[Authorize(Roles = Role.Receiver.FullyAuth)]
|
||||||
public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto)
|
public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto)
|
||||||
{
|
{
|
||||||
var authReceiverMail = User.GetAuthReceiverMail();
|
var authReceiverMail = User.GetReceiverMailOfReceiver();
|
||||||
if (authReceiverMail is null)
|
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));
|
_logger.LogError("EmailAddress claim is not found in envelope-receiver-read-only creation process. Create DTO is:\n {dto}", JsonConvert.SerializeObject(createDto));
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
var envelopeId = User.GetAuthEnvelopeId();
|
var envelopeId = User.GetEnvelopeIdOfReceiver();
|
||||||
if (envelopeId is null)
|
|
||||||
{
|
|
||||||
_logger.LogError("Envelope Id claim is not found in envelope-receiver-read-only creation process. Create DTO is:\n {dto}", JsonConvert.SerializeObject(createDto));
|
|
||||||
return Unauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
createDto.AddedWho = authReceiverMail;
|
createDto.AddedWho = authReceiverMail;
|
||||||
createDto.EnvelopeId = envelopeId;
|
createDto.EnvelopeId = envelopeId;
|
||||||
@@ -3,8 +3,7 @@ using EnvelopeGenerator.Application.Common.Extensions;
|
|||||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||||
using EnvelopeGenerator.Application.Resources;
|
using EnvelopeGenerator.Application.Resources;
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
using EnvelopeGenerator.GeneratorAPI.Extensions;
|
using EnvelopeGenerator.API.Models;
|
||||||
using EnvelopeGenerator.GeneratorAPI.Models;
|
|
||||||
using Ganss.Xss;
|
using Ganss.Xss;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
@@ -13,7 +12,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
namespace EnvelopeGenerator.API.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Exposes endpoints for registering and managing two-factor authentication for envelope receivers.
|
/// Exposes endpoints for registering and managing two-factor authentication for envelope receivers.
|
||||||
@@ -112,7 +111,7 @@ public class TfaRegistrationController : ControllerBase
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs out the envelope receiver from cookie authentication.
|
/// Logs out the envelope receiver from cookie authentication.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Authorize(Roles = ReceiverRole.FullyAuth)]
|
[Authorize(Roles = Role.FullyAuth)]
|
||||||
[HttpPost("auth/logout")]
|
[HttpPost("auth/logout")]
|
||||||
public async Task<IActionResult> LogOutAsync()
|
public async Task<IActionResult> LogOutAsync()
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
17
EnvelopeGenerator.API/EnvelopeClaimTypes.cs
Normal file
17
EnvelopeGenerator.API/EnvelopeClaimTypes.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
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)}";
|
||||||
|
}
|
||||||
@@ -17,6 +17,17 @@
|
|||||||
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="ClientApp\**" />
|
||||||
|
<Content Remove="ClientApp\**" />
|
||||||
|
<EmbeddedResource Remove="ClientApp\**" />
|
||||||
|
<None Remove="ClientApp\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="yarp.json" CopyToOutputDirectory="PreserveNewest" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AspNetCore.Scalar" Version="1.1.8" />
|
<PackageReference Include="AspNetCore.Scalar" Version="1.1.8" />
|
||||||
<PackageReference Include="DigitalData.Auth.Client" Version="1.3.7" />
|
<PackageReference Include="DigitalData.Auth.Client" Version="1.3.7" />
|
||||||
@@ -28,6 +39,7 @@
|
|||||||
<PackageReference Include="Scalar.AspNetCore" Version="2.2.1" />
|
<PackageReference Include="Scalar.AspNetCore" Version="2.2.1" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
||||||
<PackageReference Include="DigitalData.EmailProfilerDispatcher.Abstraction" Version="3.2.0" />
|
<PackageReference Include="DigitalData.EmailProfilerDispatcher.Abstraction" Version="3.2.0" />
|
||||||
|
<PackageReference Include="Yarp.ReverseProxy" Version="2.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
||||||
@@ -49,41 +61,13 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="ClientApp\" />
|
<Folder Include="wwwroot\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
|
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.CommonServices\EnvelopeGenerator.CommonServices.vbproj" />
|
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
|
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
|
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Update="ClientApp\envelope-generator-ui\dist\envelope-generator-ui\browser\chunk-35PBLQEP.js">
|
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="ClientApp\envelope-generator-ui\dist\envelope-generator-ui\browser\chunk-IUSOII6E.js">
|
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="ClientApp\envelope-generator-ui\dist\envelope-generator-ui\browser\favicon.ico">
|
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="ClientApp\envelope-generator-ui\dist\envelope-generator-ui\browser\index.html">
|
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="ClientApp\envelope-generator-ui\dist\envelope-generator-ui\browser\login\index.html">
|
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="ClientApp\envelope-generator-ui\dist\envelope-generator-ui\browser\main-ZN7C4PGY.js">
|
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="ClientApp\envelope-generator-ui\dist\envelope-generator-ui\browser\polyfills-N6LQB2YD.js">
|
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="ClientApp\envelope-generator-ui\dist\envelope-generator-ui\browser\styles-S5ZWIC2V.css">
|
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,55 +1,69 @@
|
|||||||
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Extensions;
|
namespace EnvelopeGenerator.API.Extensions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides helper methods for working with envelope-specific authentication claims.
|
/// Provides helper methods for working with envelope-specific authentication claims.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class EnvelopeAuthExtensions
|
public static class ReceiverClaimExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
private static string GetRequiredClaimOfReceiver(this ClaimsPrincipal user, string claimType)
|
||||||
/// Retrieves a claim value by type.
|
{
|
||||||
/// </summary>
|
var value = user.FindFirstValue(claimType);
|
||||||
/// <param name="user">The current claims principal.</param>
|
if (value is not null)
|
||||||
/// <param name="claimType">The claim type to resolve.</param>
|
{
|
||||||
/// <returns>The claim value or null when missing.</returns>
|
return value;
|
||||||
public static string? GetClaimValue(this ClaimsPrincipal user, string claimType) => user.FindFirstValue(claimType);
|
}
|
||||||
|
|
||||||
|
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>
|
/// <summary>
|
||||||
/// Gets the authenticated envelope UUID from the claims.
|
/// Gets the authenticated envelope UUID from the claims.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string? GetAuthEnvelopeUuid(this ClaimsPrincipal user) => user.FindFirstValue(ClaimTypes.NameIdentifier);
|
public static string GetEnvelopeUuidOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.NameIdentifier);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the authenticated receiver signature from the claims.
|
/// Gets the authenticated receiver signature from the claims.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string? GetAuthReceiverSignature(this ClaimsPrincipal user) => user.FindFirstValue(ClaimTypes.Hash);
|
public static string GetReceiverSignatureOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.Hash);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the authenticated receiver display name from the claims.
|
/// Gets the authenticated receiver display name from the claims.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string? GetAuthReceiverName(this ClaimsPrincipal user) => user.FindFirstValue(ClaimTypes.Name);
|
public static string GetReceiverNameOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.Name);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the authenticated receiver email address from the claims.
|
/// Gets the authenticated receiver email address from the claims.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string? GetAuthReceiverMail(this ClaimsPrincipal user) => user.FindFirstValue(ClaimTypes.Email);
|
public static string GetReceiverMailOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.Email);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the authenticated envelope title from the claims.
|
/// Gets the authenticated envelope title from the claims.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string? GetAuthEnvelopeTitle(this ClaimsPrincipal user) => user.FindFirstValue(EnvelopeClaimTypes.Title);
|
public static string GetEnvelopeTitleOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(EnvelopeClaimTypes.Title);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the authenticated envelope identifier from the claims.
|
/// Gets the authenticated envelope identifier from the claims.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static int? GetAuthEnvelopeId(this ClaimsPrincipal user)
|
public static int GetEnvelopeIdOfReceiver(this ClaimsPrincipal user)
|
||||||
{
|
{
|
||||||
var envIdStr = user.FindFirstValue(EnvelopeClaimTypes.Id);
|
var envIdStr = user.GetRequiredClaimOfReceiver(EnvelopeClaimTypes.Id);
|
||||||
return int.TryParse(envIdStr, out var envId) ? envId : null;
|
if (!int.TryParse(envIdStr, out var envId))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Claim '{EnvelopeClaimTypes.Id}' is not a valid integer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return envId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
95
EnvelopeGenerator.API/Extensions/SenderClaimExtensions.cs
Normal file
95
EnvelopeGenerator.API/Extensions/SenderClaimExtensions.cs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI.Middleware;
|
namespace EnvelopeGenerator.API.Middleware;
|
||||||
|
|
||||||
using DigitalData.Core.Exceptions;
|
using DigitalData.Core.Exceptions;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI.Models;
|
namespace EnvelopeGenerator.API.Models;
|
||||||
|
|
||||||
public record Auth(string? AccessCode = null, string? SmsCode = null, string? AuthenticatorCode = null, bool UserSelectSMS = default)
|
public record Auth(string? AccessCode = null, string? SmsCode = null, string? AuthenticatorCode = null, bool UserSelectSMS = default)
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI.Models;
|
namespace EnvelopeGenerator.API.Models;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the keys and default values used for authentication token handling
|
/// Represents the keys and default values used for authentication token handling
|
||||||
@@ -24,5 +24,5 @@ public class AuthTokenKeys
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the expected audience value for the authentication token.
|
/// Gets the expected audience value for the authentication token.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Audience { get; init; } = "sign-flow-gen.digitaldata.works";
|
public string Audience { get; init; } = "sign-flow.digitaldata.works";
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI.Models;
|
namespace EnvelopeGenerator.API.Models;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI.Models
|
namespace EnvelopeGenerator.API.Models
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a hyperlink for contact purposes with various HTML attributes.
|
/// Represents a hyperlink for contact purposes with various HTML attributes.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Models;
|
namespace EnvelopeGenerator.API.Models;
|
||||||
|
|
||||||
public class Culture
|
public class Culture
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI.Models;
|
namespace EnvelopeGenerator.API.Models;
|
||||||
|
|
||||||
public class Cultures : List<Culture>
|
public class Cultures : List<Culture>
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI.Models;
|
namespace EnvelopeGenerator.API.Models;
|
||||||
|
|
||||||
public class CustomImages : Dictionary<string, Image>
|
public class CustomImages : Dictionary<string, Image>
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI.Models;
|
namespace EnvelopeGenerator.API.Models;
|
||||||
|
|
||||||
public class ErrorViewModel
|
public class ErrorViewModel
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI.Models;
|
namespace EnvelopeGenerator.API.Models;
|
||||||
|
|
||||||
public class Image
|
public class Image
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Models;
|
namespace EnvelopeGenerator.API.Models;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Repräsentiert ein Login-Modell mit erforderlichem Passwort und optionaler ID und Benutzername.
|
/// Repräsentiert ein Login-Modell mit erforderlichem Passwort und optionaler ID und Benutzername.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI.Models;
|
namespace EnvelopeGenerator.API.Models;
|
||||||
|
|
||||||
public class MainViewModel
|
public class MainViewModel
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Text.Json.Serialization;
|
using EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Models.PsPdfKitAnnotation;
|
namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
||||||
|
|
||||||
public record Annotation : IAnnotation
|
public record Annotation : IAnnotation
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Text.Json.Serialization;
|
using EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Models.PsPdfKitAnnotation;
|
namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
||||||
|
|
||||||
public class AnnotationParams
|
public class AnnotationParams
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Models.PsPdfKitAnnotation;
|
namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Background is an annotation for the PSPDF Kit. However, it has no function.
|
/// The Background is an annotation for the PSPDF Kit. However, it has no function.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI.Models.PsPdfKitAnnotation;
|
namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
||||||
|
|
||||||
public record Color
|
public record Color
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI.Models.PsPdfKitAnnotation;
|
namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
||||||
|
|
||||||
public static class Extensions
|
public static class Extensions
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI.Models.PsPdfKitAnnotation;
|
namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation;
|
||||||
|
|
||||||
public interface IAnnotation
|
public interface IAnnotation
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI.Models;
|
namespace EnvelopeGenerator.API.Models;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the parameters for two-factor authentication (2FA) registration.
|
/// Represents the parameters for two-factor authentication (2FA) registration.
|
||||||
@@ -11,11 +11,11 @@ using DigitalData.UserManager.DependencyInjection;
|
|||||||
using EnvelopeGenerator.Application;
|
using EnvelopeGenerator.Application;
|
||||||
using DigitalData.Auth.Client;
|
using DigitalData.Auth.Client;
|
||||||
using DigitalData.Core.Abstractions;
|
using DigitalData.Core.Abstractions;
|
||||||
using EnvelopeGenerator.GeneratorAPI.Models;
|
using EnvelopeGenerator.API.Models;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using DigitalData.Core.Abstractions.Security.Extensions;
|
using DigitalData.Core.Abstractions.Security.Extensions;
|
||||||
using EnvelopeGenerator.GeneratorAPI.Middleware;
|
using EnvelopeGenerator.API.Middleware;
|
||||||
using NLog.Web;
|
using NLog.Web;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
@@ -26,6 +26,8 @@ try
|
|||||||
{
|
{
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
builder.Configuration.AddJsonFile("yarp.json", optional: true, reloadOnChange: true);
|
||||||
|
|
||||||
builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
|
builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
|
||||||
|
|
||||||
if (!builder.Environment.IsDevelopment())
|
if (!builder.Environment.IsDevelopment())
|
||||||
@@ -39,6 +41,8 @@ try
|
|||||||
var deferredProvider = new DeferredServiceProvider();
|
var deferredProvider = new DeferredServiceProvider();
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
|
builder.Services.AddHttpClient();
|
||||||
|
builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
|
||||||
|
|
||||||
// CORS Policy
|
// CORS Policy
|
||||||
var allowedOrigins = config.GetSection("AllowedOrigins").Get<string[]>() ??
|
var allowedOrigins = config.GetSection("AllowedOrigins").Get<string[]>() ??
|
||||||
@@ -102,6 +106,8 @@ try
|
|||||||
{
|
{
|
||||||
options.IncludeXmlComments(xmlFile);
|
options.IncludeXmlComments(xmlFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options.DocumentFilter<EnvelopeGenerator.API.Documentation.AuthProxyDocumentFilter>();
|
||||||
});
|
});
|
||||||
builder.Services.AddOpenApi();
|
builder.Services.AddOpenApi();
|
||||||
|
|
||||||
@@ -241,6 +247,7 @@ try
|
|||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.MapReverseProxy();
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
@@ -6,11 +6,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AuthClientParams": {
|
"AuthClientParams": {
|
||||||
"Url": "https://localhost:7192/auth-hub",
|
"Url": "http://172.24.12.39:9090/auth-hub",
|
||||||
"PublicKeys": [
|
"PublicKeys": [
|
||||||
{
|
{
|
||||||
"Issuer": "auth.digitaldata.works",
|
"Issuer": "auth.digitaldata.works",
|
||||||
"Audience": "sign-flow-gen.digitaldata.works"
|
"Audience": "sign-flow.digitaldata.works"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"RetryDelay": "00:00:05"
|
"RetryDelay": "00:00:05"
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"AllowedOrigins": [ "http://localhost:4200" ],
|
"AllowedOrigins": [ "http://localhost:4200", "http://172.24.12.39:9090", "https://localhost:8088", "http://localhost:5131", "http://localhost:7192" ],
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"Default": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;",
|
"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;"
|
"DbMigrationTest": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM_DATA_MIGR_TEST;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
|
||||||
@@ -24,11 +24,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AuthClientParams": {
|
"AuthClientParams": {
|
||||||
"Url": "https://localhost:7192/auth-hub",
|
"Url": "http://172.24.12.39:9090/auth-hub",
|
||||||
"PublicKeys": [
|
"PublicKeys": [
|
||||||
{
|
{
|
||||||
"Issuer": "auth.digitaldata.works",
|
"Issuer": "auth.digitaldata.works",
|
||||||
"Audience": "sign-flow-gen.digitaldata.works"
|
"Audience": "sign-flow.digitaldata.works"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"RetryDelay": "00:00:05"
|
"RetryDelay": "00:00:05"
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
"Cookie": "AuthToken",
|
"Cookie": "AuthToken",
|
||||||
"QueryString": "AuthToken",
|
"QueryString": "AuthToken",
|
||||||
"Issuer": "auth.digitaldata.works",
|
"Issuer": "auth.digitaldata.works",
|
||||||
"Audience": "work-flow.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==",
|
"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.
|
"Content-Security-Policy": [ // The first format parameter {0} will be replaced by the nonce value.
|
||||||
25
EnvelopeGenerator.API/yarp.json
Normal file
25
EnvelopeGenerator.API/yarp.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,19 @@ using System.Text;
|
|||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions
|
namespace EnvelopeGenerator.Application.Common.Extensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
public static class LoggerExtensions
|
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)
|
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());
|
var sb = new StringBuilder().AppendLine(envelopeReceiverId.DecodeEnvelopeReceiverId().ToTitle());
|
||||||
@@ -18,6 +29,15 @@ namespace EnvelopeGenerator.Application.Common.Extensions
|
|||||||
logger.Log(LogLevel.Error, exception, sb.AppendLine(exception.Message).ToString(), args);
|
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)
|
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}");
|
var sb = new StringBuilder($"Envelope Uuid: {uuid}");
|
||||||
@@ -34,6 +54,11 @@ namespace EnvelopeGenerator.Application.Common.Extensions
|
|||||||
logger.Log(LogLevel.Error, exception, sb.ToString(), args);
|
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)
|
public static string ToTitle(this (string? UUID, string? Signature) envelopeReceiverTuple)
|
||||||
{
|
{
|
||||||
return $"UUID is {envelopeReceiverTuple.UUID} and signature is {envelopeReceiverTuple.Signature}";
|
return $"UUID is {envelopeReceiverTuple.UUID} and signature is {envelopeReceiverTuple.Signature}";
|
||||||
|
|||||||
@@ -2,16 +2,42 @@
|
|||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions
|
namespace EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static class XSSExtensions
|
||||||
{
|
{
|
||||||
public static class XSSExtensions
|
/// <summary>
|
||||||
{
|
///
|
||||||
public static string? TryEncode(this string? value, UrlEncoder encoder) => value is null ? value : encoder.Encode(value);
|
/// </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);
|
||||||
|
|
||||||
public static string? TryEncode(this LocalizedString? value, UrlEncoder encoder) => value is null ? null : 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);
|
||||||
|
|
||||||
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 string? html, HtmlSanitizer sanitizer) => html is null ? html : sanitizer.Sanitize(html);
|
||||||
|
|
||||||
public static string? TrySanitize(this LocalizedString? html, HtmlSanitizer sanitizer) => html is null ? null : 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);
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ using EnvelopeGenerator.Domain.Entities;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
using DigitalData.Core.Exceptions;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Documents.Queries;
|
namespace EnvelopeGenerator.Application.Documents.Queries;
|
||||||
|
|
||||||
@@ -12,14 +13,14 @@ namespace EnvelopeGenerator.Application.Documents.Queries;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Id">The unique identifier of the document. Optional.</param>
|
/// <param name="Id">The unique identifier of the document. Optional.</param>
|
||||||
/// <param name="EnvelopeId">The identifier of the envelope associated with the document. Optional.</param>
|
/// <param name="EnvelopeId">The identifier of the envelope associated with the document. Optional.</param>
|
||||||
public record ReadDocumentQuery(int? Id = null, int? EnvelopeId = null) : IRequest<DocumentDto?>
|
public record ReadDocumentQuery(int? Id = null, int? EnvelopeId = null) : IRequest<DocumentDto>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles queries for reading <see cref="Document"/> data based on either the document ID or the envelope ID.
|
/// Handles queries for reading <see cref="Document"/> data based on either the document ID or the envelope ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ReadDocumentQueryHandler : IRequestHandler<ReadDocumentQuery, DocumentDto?>
|
public class ReadDocumentQueryHandler : IRequestHandler<ReadDocumentQuery, DocumentDto>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// TempRepo for accessing <see cref="Document"/> entities.
|
/// TempRepo for accessing <see cref="Document"/> entities.
|
||||||
@@ -50,20 +51,19 @@ public class ReadDocumentQueryHandler : IRequestHandler<ReadDocumentQuery, Docum
|
|||||||
/// <exception cref="InvalidOperationException">
|
/// <exception cref="InvalidOperationException">
|
||||||
/// Thrown when neither <see cref="ReadDocumentQuery.Id"/> nor <see cref="ReadDocumentQuery.EnvelopeId"/> is provided.
|
/// Thrown when neither <see cref="ReadDocumentQuery.Id"/> nor <see cref="ReadDocumentQuery.EnvelopeId"/> is provided.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public async Task<DocumentDto?> Handle(ReadDocumentQuery query, CancellationToken cancel)
|
public async Task<DocumentDto> Handle(ReadDocumentQuery query, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
if (query.Id is not null)
|
if (query.Id is not null)
|
||||||
{
|
{
|
||||||
var doc = await _repo.ReadOnly().Where(d => d.Id == query.Id).FirstOrDefaultAsync(cancel);
|
var doc = await _repo.Query.Where(d => d.Id == query.Id).FirstOrDefaultAsync(cancel);
|
||||||
return _mapper.Map<DocumentDto>(doc);
|
return _mapper.Map<DocumentDto>(doc);
|
||||||
}
|
}
|
||||||
else if (query.EnvelopeId is not null)
|
else if (query.EnvelopeId is not null)
|
||||||
{
|
{
|
||||||
var doc = await _repo.ReadOnly().Where(d => d.EnvelopeId == query.EnvelopeId).FirstOrDefaultAsync(cancel);
|
var doc = await _repo.Query.Where(d => d.EnvelopeId == query.EnvelopeId).FirstOrDefaultAsync(cancel);
|
||||||
return _mapper.Map<DocumentDto>(doc);
|
return _mapper.Map<DocumentDto>(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException(
|
throw new NotFoundException();
|
||||||
$"Invalid {nameof(ReadDocumentQuery)}: either {nameof(query.Id)} or {nameof(query.EnvelopeId)} must be provided.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@ public class UpdateEmailTemplateCommandHandler : IRequestHandler<UpdateEmailTemp
|
|||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="repository"></param>
|
/// <param name="repository"></param>
|
||||||
|
/// <param name="mapper"></param>
|
||||||
public UpdateEmailTemplateCommandHandler(IRepository<EmailTemplate> repository, IMapper mapper)
|
public UpdateEmailTemplateCommandHandler(IRepository<EmailTemplate> repository, IMapper mapper)
|
||||||
{
|
{
|
||||||
_repository = repository;
|
_repository = repository;
|
||||||
|
|||||||
@@ -67,10 +67,11 @@ public static class Extensions
|
|||||||
/// <param name="key"></param>
|
/// <param name="key"></param>
|
||||||
/// <param name="cancel"></param>
|
/// <param name="cancel"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Task<EnvelopeReceiverDto?> ReadEnvelopeReceiverAsync(this IMediator mediator, string key, CancellationToken cancel = default)
|
public static async Task<EnvelopeReceiverDto?> ReadEnvelopeReceiverAsync(this IMediator mediator, string key, CancellationToken cancel = default)
|
||||||
{
|
{
|
||||||
var q = new ReadEnvelopeReceiverQuery() { Key = key };
|
var q = new ReadEnvelopeReceiverQuery() { Key = key };
|
||||||
return mediator.Send(q, cancel).Then(envRcvs => envRcvs.FirstOrDefault());
|
var envRcvs = await mediator.Send(q, cancel);
|
||||||
|
return envRcvs.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -81,12 +82,13 @@ public static class Extensions
|
|||||||
/// <param name="signature"></param>
|
/// <param name="signature"></param>
|
||||||
/// <param name="cancel"></param>
|
/// <param name="cancel"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Task<EnvelopeReceiverDto?> ReadEnvelopeReceiverAsync(this IMediator mediator, string uuid, string signature, CancellationToken cancel = default)
|
public static async Task<EnvelopeReceiverDto?> ReadEnvelopeReceiverAsync(this IMediator mediator, string uuid, string signature, CancellationToken cancel = default)
|
||||||
{
|
{
|
||||||
var q = new ReadEnvelopeReceiverQuery();
|
var q = new ReadEnvelopeReceiverQuery();
|
||||||
q.Envelope.Uuid = uuid;
|
q.Envelope.Uuid = uuid;
|
||||||
q.Receiver.Signature = signature;
|
q.Receiver.Signature = signature;
|
||||||
return mediator.Send(q, cancel).Then(envRcvs => envRcvs.FirstOrDefault());
|
var envRcvs = await mediator.Send(q, cancel);
|
||||||
|
return envRcvs.FirstOrDefault();
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Verarbeitet <see cref="ReadEnvelopeReceiverQuery"/> und liefert passende <see cref="EnvelopeReceiverDto"/>-Ergebnisse.
|
/// Verarbeitet <see cref="ReadEnvelopeReceiverQuery"/> und liefert passende <see cref="EnvelopeReceiverDto"/>-Ergebnisse.
|
||||||
|
|||||||
@@ -68,6 +68,6 @@ public class ReceiverAlreadySignedQueryHandler : IRequestHandler<ReceiverAlready
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<bool> Handle(ReceiverAlreadySignedQuery request, CancellationToken cancel = default)
|
public async Task<bool> Handle(ReceiverAlreadySignedQuery request, CancellationToken cancel = default)
|
||||||
{
|
{
|
||||||
return await _repo.ReadOnly().Where(request).Where(h => h.Status == EnvelopeStatus.DocumentSigned).AnyAsync(cancel);
|
return await _repo.Query.Where(request).Where(h => h.Status == EnvelopeStatus.DocumentSigned).AnyAsync(cancel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ public class CreateHistoryCommandHandler : IRequestHandler<CreateHistoryCommand,
|
|||||||
if(request.UserReference is null)
|
if(request.UserReference is null)
|
||||||
{
|
{
|
||||||
var receivers = await _erRepo
|
var receivers = await _erRepo
|
||||||
.ReadOnly()
|
.Query
|
||||||
.Where(request)
|
.Where(request)
|
||||||
.Include(er => er.Receiver)
|
.Include(er => er.Receiver)
|
||||||
.ToListAsync(cancel);
|
.ToListAsync(cancel);
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ public class CreateReceiverCommandHandler : IRequestHandler<CreateReceiverComman
|
|||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="repo"></param>
|
/// <param name="repo"></param>
|
||||||
|
/// <param name="mapper"></param>
|
||||||
public CreateReceiverCommandHandler(IRepository<Receiver> repo, IMapper mapper)
|
public CreateReceiverCommandHandler(IRepository<Receiver> repo, IMapper mapper)
|
||||||
{
|
{
|
||||||
_repo = repo;
|
_repo = repo;
|
||||||
@@ -81,7 +82,7 @@ public class CreateReceiverCommandHandler : IRequestHandler<CreateReceiverComman
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<(ReceiverDto Receiver, bool AlreadyExists)> Handle(CreateReceiverCommand request, CancellationToken cancel)
|
public async Task<(ReceiverDto Receiver, bool AlreadyExists)> Handle(CreateReceiverCommand request, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
var receiver = await _repo.ReadOnly()
|
var receiver = await _repo.Query
|
||||||
.Where(r => r.EmailAddress == request.EmailAddress)
|
.Where(r => r.EmailAddress == request.EmailAddress)
|
||||||
.SingleOrDefaultAsync(cancel);
|
.SingleOrDefaultAsync(cancel);
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Domain.Constants
|
|
||||||
{
|
|
||||||
public static class ReceiverRole
|
|
||||||
{
|
|
||||||
public const string PreAuth = "PreAuth";
|
|
||||||
public const string FullyAuth = "FullyAuth";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
23
EnvelopeGenerator.Domain/Constants/Role.cs
Normal file
23
EnvelopeGenerator.Domain/Constants/Role.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#if NETFRAMEWORK
|
||||||
|
using System;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Domain.Constants
|
||||||
|
{
|
||||||
|
public static class Role
|
||||||
|
{
|
||||||
|
[Obsolete("Use Receiver.PreAuth or Receiver.FullyAuth")]
|
||||||
|
public const string PreAuth = "PreAuth";
|
||||||
|
|
||||||
|
[Obsolete("Use Receiver.PreAuth or Receiver.FullyAuth")]
|
||||||
|
public const string FullyAuth = "FullyAuth";
|
||||||
|
|
||||||
|
public static class Receiver
|
||||||
|
{
|
||||||
|
public const string PreAuth = "PreAuth";
|
||||||
|
public const string FullyAuth = "FullyAuth";
|
||||||
|
}
|
||||||
|
|
||||||
|
public const string Sender = "Sender";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"isRoot": true,
|
|
||||||
"tools": {
|
|
||||||
"dotnet-ef": {
|
|
||||||
"version": "9.0.3",
|
|
||||||
"commands": [
|
|
||||||
"dotnet-ef"
|
|
||||||
],
|
|
||||||
"rollForward": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application;
|
|
||||||
using DigitalData.UserManager.Application.Contracts;
|
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using EnvelopeGenerator.GeneratorAPI.Models;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Controller verantwortlich für die Benutzer-Authentifizierung, einschließlich Anmelden, Abmelden und Überprüfung des Authentifizierungsstatus.
|
|
||||||
/// </summary>
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
[ApiController]
|
|
||||||
public partial class AuthController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly ILogger<AuthController> _logger;
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
private readonly IUserService _userService;
|
|
||||||
private readonly IDirectorySearchService _dirSearchService;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="AuthController"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger">The logger instance.</param>
|
|
||||||
/// <param name="userService">The user service instance.</param>
|
|
||||||
/// <param name="dirSearchService">The directory search service instance.</param>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public AuthController(ILogger<AuthController> logger, IUserService userService, IDirectorySearchService dirSearchService)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_userService = userService;
|
|
||||||
_dirSearchService = dirSearchService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Authentifiziert einen Benutzer und generiert ein JWT-Token. Wenn 'cookie' wahr ist, wird das Token als HTTP-Only-Cookie zurückgegeben.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="login">Benutzeranmeldedaten (Benutzername und Passwort).</param>
|
|
||||||
/// <param name="cookie">Wenn wahr, wird das JWT-Token auch als HTTP-Only-Cookie gesendet.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// Gibt eine HTTP 200 oder 401.
|
|
||||||
/// </returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// Sample request:
|
|
||||||
///
|
|
||||||
/// POST /api/auth?cookie=true
|
|
||||||
/// {
|
|
||||||
/// "username": "MaxMustermann",
|
|
||||||
/// "password": "Geheim123!"
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// POST /api/auth?cookie=true
|
|
||||||
/// {
|
|
||||||
/// "id": "1",
|
|
||||||
/// "password": "Geheim123!"
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// </remarks>
|
|
||||||
/// <response code="200">Erfolgreiche Anmeldung. Gibt das JWT-Token im Antwortkörper oder als Cookie zurück, wenn 'cookie' wahr ist.</response>
|
|
||||||
/// <response code="401">Unbefugt. Ungültiger Benutzername oder Passwort.</response>
|
|
||||||
[ProducesResponseType(typeof(string), StatusCodes.Status200OK, "text/javascript")]
|
|
||||||
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
|
||||||
[AllowAnonymous]
|
|
||||||
[HttpPost]
|
|
||||||
public Task<IActionResult> Login([FromBody] Login login, [FromQuery] bool cookie = false)
|
|
||||||
{
|
|
||||||
// added to configure open API (swagger and scalar)
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Authentifiziert einen Benutzer und generiert ein JWT-Token. Das Token wird als HTTP-only-Cookie zurückgegeben.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="login">Benutzeranmeldedaten (Benutzername und Passwort).</param>
|
|
||||||
/// <returns>
|
|
||||||
/// Gibt eine HTTP 200 oder 401.
|
|
||||||
/// </returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// Sample request:
|
|
||||||
///
|
|
||||||
/// POST /api/auth/form
|
|
||||||
/// {
|
|
||||||
/// "username": "MaxMustermann",
|
|
||||||
/// "password": "Geheim123!"
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// </remarks>
|
|
||||||
/// <response code="200">Erfolgreiche Anmeldung. Gibt das JWT-Token im Antwortkörper oder als Cookie zurück, wenn 'cookie' wahr ist.</response>
|
|
||||||
/// <response code="401">Unbefugt. Ungültiger Benutzername oder Passwort.</response>
|
|
||||||
[ProducesResponseType(typeof(string), StatusCodes.Status200OK, "text/javascript")]
|
|
||||||
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
|
||||||
[AllowAnonymous]
|
|
||||||
[HttpPost]
|
|
||||||
[Route("form")]
|
|
||||||
public Task<IActionResult> Login([FromForm] Login login)
|
|
||||||
{
|
|
||||||
// added to configure open API (swagger and scalar)
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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,62 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Security.Claims;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides extension methods for extracting user information from a <see cref="ClaimsPrincipal"/>.
|
|
||||||
/// </summary>
|
|
||||||
public static class ControllerExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Attempts to retrieve the user's ID from the claims. Returns null if the ID is not found or invalid.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
||||||
/// <returns>The user's ID as an integer, or null if not found or invalid.</returns>
|
|
||||||
public static int? GetIdOrDefault(this ClaimsPrincipal user)
|
|
||||||
=> int.TryParse(user.FindFirstValue(ClaimTypes.NameIdentifier) ?? user.FindFirstValue("sub"), out int result)
|
|
||||||
? result : null;
|
|
||||||
|
|
||||||
/// <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)
|
|
||||||
=> user.GetIdOrDefault()
|
|
||||||
?? throw new InvalidOperationException("User ID claim is missing or invalid. This may indicate a misconfigured or forged JWT token.");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the username from the claims, if available.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
||||||
/// <returns>The username as a string, or null if not found.</returns>
|
|
||||||
public static string? GetUsernameOrDefault(this ClaimsPrincipal user)
|
|
||||||
=> user.FindFirst(ClaimTypes.Name)?.Value;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the user's surname (last name) from the claims, if available.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
||||||
/// <returns>The surname as a string, or null if not found.</returns>
|
|
||||||
public static string? GetNameOrDefault(this ClaimsPrincipal user)
|
|
||||||
=> user.FindFirst(ClaimTypes.Surname)?.Value;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the user's given name (first name) from the claims, if available.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
||||||
/// <returns>The given name as a string, or null if not found.</returns>
|
|
||||||
public static string? GetPrenameOrDefault(this ClaimsPrincipal user)
|
|
||||||
=> user.FindFirst(ClaimTypes.GivenName)?.Value;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the user's email address from the claims, if available.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
||||||
/// <returns>The email address as a string, or null if not found.</returns>
|
|
||||||
public static string? GetEmailOrDefault(this ClaimsPrincipal user)
|
|
||||||
=> user.FindFirst(ClaimTypes.Email)?.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides access to envelope documents for authenticated receivers.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Initializes a new instance of the <see cref="DocumentController"/> class.
|
|
||||||
/// </remarks>
|
|
||||||
[Authorize(Roles = ReceiverRole.FullyAuth)]
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
public class DocumentController(IMediator mediator, ILogger<DocumentController> logger) : ControllerBase
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the document bytes for the specified envelope receiver key.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="query">Encoded envelope key.</param>
|
|
||||||
/// <param name="cancel">Cancellation token.</param>
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetDocument(ReadEnvelopeReceiverQuery query, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
var envRcv = await mediator.Send(query, cancel).FirstAsync(Exceptions.NotFound);
|
|
||||||
|
|
||||||
var byteData = envRcv.Envelope?.Documents?.FirstOrDefault()?.ByteData;
|
|
||||||
|
|
||||||
if (byteData is null || byteData.Length == 0)
|
|
||||||
{
|
|
||||||
logger.LogError("Document byte data is null or empty for envelope-receiver entity:\n{envelopeKey}.",
|
|
||||||
envRcv.ToJson(Format.Json.ForDiagnostics));
|
|
||||||
throw new NotFoundException("Document is empty.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return File(byteData, "application/octet-stream");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.GeneratorAPI
|
|
||||||
{
|
|
||||||
/// <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,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
https://go.microsoft.com/fwlink/?LinkID=208121.
|
|
||||||
-->
|
|
||||||
<Project>
|
|
||||||
<PropertyGroup>
|
|
||||||
<WebPublishMethod>Package</WebPublishMethod>
|
|
||||||
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
|
|
||||||
<LastUsedPlatform>Any CPU</LastUsedPlatform>
|
|
||||||
<SiteUrlToLaunchAfterPublish />
|
|
||||||
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
|
|
||||||
<ExcludeApp_Data>false</ExcludeApp_Data>
|
|
||||||
<ProjectGuid>4fdae4ba-f512-444a-9e18-111047d3ef02</ProjectGuid>
|
|
||||||
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\signFLOW\Gen\Api\net7\win64\$(Version)\$(Version).zip</DesktopBuildPackageLocation>
|
|
||||||
<PackageAsSingleFile>true</PackageAsSingleFile>
|
|
||||||
<DeployIisAppPath>SignFlowGen</DeployIisAppPath>
|
|
||||||
<_TargetId>IISWebDeployPackage</_TargetId>
|
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
|
||||||
<SelfContained>true</SelfContained>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
https://go.microsoft.com/fwlink/?LinkID=208121.
|
|
||||||
-->
|
|
||||||
<Project>
|
|
||||||
<PropertyGroup>
|
|
||||||
<WebPublishMethod>Package</WebPublishMethod>
|
|
||||||
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
|
|
||||||
<LastUsedPlatform>Any CPU</LastUsedPlatform>
|
|
||||||
<SiteUrlToLaunchAfterPublish />
|
|
||||||
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
|
|
||||||
<ExcludeApp_Data>false</ExcludeApp_Data>
|
|
||||||
<ProjectGuid>4fdae4ba-f512-444a-9e18-111047d3ef02</ProjectGuid>
|
|
||||||
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\signFLOW\Gen\Api\net9\win64\$(Version)\$(Version).zip</DesktopBuildPackageLocation>
|
|
||||||
<PackageAsSingleFile>true</PackageAsSingleFile>
|
|
||||||
<DeployIisAppPath>SignFlowGen</DeployIisAppPath>
|
|
||||||
<_TargetId>IISWebDeployPackage</_TargetId>
|
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
|
||||||
<SelfContained>true</SelfContained>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="3em" height="2.5em" viewBox="0 0 24 24" style="stroke: #a9a8ad;">
|
|
||||||
<path opacity="0.5" d="M15.9998 2L14.9998 2C12.1714 2 10.7576 2.00023 9.87891 2.87891C9.00023 3.75759 9.00023 5.1718 9.00023 8.00023L9.00023 16.0002C9.00023 18.8287 9.00023 20.2429 9.87891 21.1215C10.7574 22 12.1706 22 14.9976 22L14.9998 22L15.9998 22C18.8282 22 20.2424 22 21.1211 21.1213C21.9998 20.2426 21.9998 18.8284 21.9998 16L21.9998 8L21.9998 7.99998C21.9998 5.17157 21.9998 3.75736 21.1211 2.87868C20.2424 2 18.8282 2 15.9998 2Z" fill="#1C274C"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.25098 11.999C1.25098 11.5848 1.58676 11.249 2.00098 11.249L13.9735 11.249L12.0129 9.56845C11.6984 9.29889 11.662 8.82541 11.9315 8.51092C12.2011 8.19642 12.6746 8.16 12.9891 8.42957L16.4891 11.4296C16.6553 11.5721 16.751 11.7801 16.751 11.999C16.751 12.218 16.6553 12.426 16.4891 12.5685L12.9891 15.5685C12.6746 15.838 12.2011 15.8016 11.9315 15.4871C11.662 15.1726 11.6984 14.6991 12.0129 14.4296L13.9735 12.749L2.00098 12.749C1.58676 12.749 1.25098 12.4132 1.25098 11.999Z" fill="#1C274C"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 264 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -15,7 +15,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
|
|
||||||
namespace EnvelopeGenerator.Web.Controllers;
|
namespace EnvelopeGenerator.Web.Controllers;
|
||||||
|
|
||||||
[Authorize(Roles = ReceiverRole.FullyAuth)]
|
[Authorize(Roles = Role.FullyAuth)]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class AnnotationController : ControllerBase
|
public class AnnotationController : ControllerBase
|
||||||
@@ -42,7 +42,7 @@ public class AnnotationController : ControllerBase
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Roles = ReceiverRole.FullyAuth)]
|
[Authorize(Roles = Role.FullyAuth)]
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> CreateOrUpdate([FromBody] PsPdfKitAnnotation? psPdfKitAnnotation = null, CancellationToken cancel = default)
|
public async Task<IActionResult> CreateOrUpdate([FromBody] PsPdfKitAnnotation? psPdfKitAnnotation = null, CancellationToken cancel = default)
|
||||||
{
|
{
|
||||||
@@ -80,7 +80,7 @@ public class AnnotationController : ControllerBase
|
|||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Roles = ReceiverRole.FullyAuth)]
|
[Authorize(Roles = Role.FullyAuth)]
|
||||||
[HttpPost("reject")]
|
[HttpPost("reject")]
|
||||||
[Obsolete("Use DigitalData.Core.Exceptions and .Middleware")]
|
[Obsolete("Use DigitalData.Core.Exceptions and .Middleware")]
|
||||||
public async Task<IActionResult> Reject([FromBody] string? reason = null)
|
public async Task<IActionResult> Reject([FromBody] string? reason = null)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
|
|
||||||
namespace EnvelopeGenerator.Web.Controllers;
|
namespace EnvelopeGenerator.Web.Controllers;
|
||||||
|
|
||||||
[Authorize(Roles = ReceiverRole.FullyAuth)]
|
[Authorize(Roles = Role.FullyAuth)]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class DocumentController : ControllerBase
|
public class DocumentController : ControllerBase
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ public class EnvelopeController : ViewControllerBase
|
|||||||
return this.ViewEnvelopeNotFound();
|
return this.ViewEnvelopeNotFound();
|
||||||
}
|
}
|
||||||
var er_secret = er_secret_res.Data;
|
var er_secret = er_secret_res.Data;
|
||||||
await HttpContext.SignInEnvelopeAsync(er_secret, ReceiverRole.FullyAuth);
|
await HttpContext.SignInEnvelopeAsync(er_secret, Role.FullyAuth);
|
||||||
return await CreateShowEnvelopeView(er_secret);
|
return await CreateShowEnvelopeView(er_secret);
|
||||||
}
|
}
|
||||||
#endregion UseAccessCode
|
#endregion UseAccessCode
|
||||||
@@ -172,7 +172,7 @@ public class EnvelopeController : ViewControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
// show envelope if already logged in
|
// show envelope if already logged in
|
||||||
if (User.IsInRole(ReceiverRole.FullyAuth))
|
if (User.IsInRole(Role.FullyAuth))
|
||||||
return await CreateShowEnvelopeView(er_secret);
|
return await CreateShowEnvelopeView(er_secret);
|
||||||
|
|
||||||
if (auth.HasMulti)
|
if (auth.HasMulti)
|
||||||
@@ -206,7 +206,7 @@ public class EnvelopeController : ViewControllerBase
|
|||||||
.WithData("ErrorMessage", _localizer.WrongEnvelopeReceiverId());
|
.WithData("ErrorMessage", _localizer.WrongEnvelopeReceiverId());
|
||||||
}
|
}
|
||||||
|
|
||||||
await HttpContext.SignInEnvelopeAsync(er_secret, ReceiverRole.FullyAuth);
|
await HttpContext.SignInEnvelopeAsync(er_secret, Role.FullyAuth);
|
||||||
|
|
||||||
return await CreateShowEnvelopeView(er_secret);
|
return await CreateShowEnvelopeView(er_secret);
|
||||||
}
|
}
|
||||||
@@ -225,9 +225,9 @@ public class EnvelopeController : ViewControllerBase
|
|||||||
&& uuidClaim == er.Envelope?.Uuid
|
&& uuidClaim == er.Envelope?.Uuid
|
||||||
&& signatureClaim is not null
|
&& signatureClaim is not null
|
||||||
&& signatureClaim == er.Receiver?.Signature
|
&& signatureClaim == er.Receiver?.Signature
|
||||||
&& User.IsInRole(ReceiverRole.FullyAuth))
|
&& User.IsInRole(Role.FullyAuth))
|
||||||
{
|
{
|
||||||
await HttpContext.SignInEnvelopeAsync(er, ReceiverRole.FullyAuth);
|
await HttpContext.SignInEnvelopeAsync(er, Role.FullyAuth);
|
||||||
|
|
||||||
//add PSPDFKit licence key
|
//add PSPDFKit licence key
|
||||||
ViewData["PSPDFKitLicenseKey"] = _configuration["PSPDFKitLicenseKey"];
|
ViewData["PSPDFKitLicenseKey"] = _configuration["PSPDFKitLicenseKey"];
|
||||||
@@ -262,7 +262,7 @@ public class EnvelopeController : ViewControllerBase
|
|||||||
return this.ViewDocumentNotFound();
|
return this.ViewDocumentNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
await HttpContext.SignInEnvelopeAsync(er, ReceiverRole.FullyAuth);
|
await HttpContext.SignInEnvelopeAsync(er, Role.FullyAuth);
|
||||||
|
|
||||||
ViewData["ReadAndConfirm"] = er.Envelope.ReadOnly;
|
ViewData["ReadAndConfirm"] = er.Envelope.ReadOnly;
|
||||||
|
|
||||||
@@ -334,7 +334,7 @@ public class EnvelopeController : ViewControllerBase
|
|||||||
await _rcvService.UpdateAsync(rcv);
|
await _rcvService.UpdateAsync(rcv);
|
||||||
}
|
}
|
||||||
|
|
||||||
await HttpContext.SignInEnvelopeAsync(er_secret, ReceiverRole.PreAuth);
|
await HttpContext.SignInEnvelopeAsync(er_secret, Role.PreAuth);
|
||||||
|
|
||||||
return await TFAViewAsync(auth.UserSelectSMS, er_secret, envelopeReceiverId);
|
return await TFAViewAsync(auth.UserSelectSMS, er_secret, envelopeReceiverId);
|
||||||
}
|
}
|
||||||
@@ -348,7 +348,7 @@ public class EnvelopeController : ViewControllerBase
|
|||||||
if (er_secret.Receiver!.TotpSecretkey is null)
|
if (er_secret.Receiver!.TotpSecretkey is null)
|
||||||
throw new InvalidOperationException($"TotpSecretkey of DTO cannot validate without TotpSecretkey. Dto: {JsonConvert.SerializeObject(er_secret)}");
|
throw new InvalidOperationException($"TotpSecretkey of DTO cannot validate without TotpSecretkey. Dto: {JsonConvert.SerializeObject(er_secret)}");
|
||||||
|
|
||||||
if (!User.IsInRole(ReceiverRole.PreAuth) || !_envSmsHandler.VerifyTotp(auth.SmsCode!, er_secret.Receiver.TotpSecretkey))
|
if (!User.IsInRole(Role.PreAuth) || !_envSmsHandler.VerifyTotp(auth.SmsCode!, er_secret.Receiver.TotpSecretkey))
|
||||||
{
|
{
|
||||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||||
ViewData["ErrorMessage"] = _localizer.WrongAccessCode();
|
ViewData["ErrorMessage"] = _localizer.WrongAccessCode();
|
||||||
@@ -364,7 +364,7 @@ public class EnvelopeController : ViewControllerBase
|
|||||||
if (er_secret.Receiver!.TotpSecretkey is null)
|
if (er_secret.Receiver!.TotpSecretkey is null)
|
||||||
throw new InvalidOperationException($"TotpSecretkey of DTO cannot validate without TotpSecretkey. Dto: {JsonConvert.SerializeObject(er_secret)}");
|
throw new InvalidOperationException($"TotpSecretkey of DTO cannot validate without TotpSecretkey. Dto: {JsonConvert.SerializeObject(er_secret)}");
|
||||||
|
|
||||||
if (!User.IsInRole(ReceiverRole.PreAuth) || !_authenticator.VerifyTotp(auth.AuthenticatorCode!, er_secret.Receiver.TotpSecretkey, window: VerificationWindow.RfcSpecifiedNetworkDelay))
|
if (!User.IsInRole(Role.PreAuth) || !_authenticator.VerifyTotp(auth.AuthenticatorCode!, er_secret.Receiver.TotpSecretkey, window: VerificationWindow.RfcSpecifiedNetworkDelay))
|
||||||
{
|
{
|
||||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||||
ViewData["ErrorMessage"] = _localizer.WrongAccessCode();
|
ViewData["ErrorMessage"] = _localizer.WrongAccessCode();
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Authorize(Roles = ReceiverRole.FullyAuth)]
|
[Authorize(Roles = Role.FullyAuth)]
|
||||||
[Obsolete("Use MediatR")]
|
[Obsolete("Use MediatR")]
|
||||||
public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto)
|
public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public class TFARegController : ViewControllerBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Roles = ReceiverRole.FullyAuth)]
|
[Authorize(Roles = Role.FullyAuth)]
|
||||||
[HttpPost("auth/logout")]
|
[HttpPost("auth/logout")]
|
||||||
public async Task<IActionResult> LogOut()
|
public async Task<IActionResult> LogOut()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EnvelopeGenerator.Infrastru
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EnvelopeGenerator.Application", "EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj", "{5A9984F8-51A2-4558-A415-EC5FEED7CF7D}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EnvelopeGenerator.Application", "EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj", "{5A9984F8-51A2-4558-A415-EC5FEED7CF7D}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EnvelopeGenerator.GeneratorAPI", "EnvelopeGenerator.GeneratorAPI\EnvelopeGenerator.GeneratorAPI.csproj", "{E5E12BA4-60C1-48BA-9053-0F8B62B38124}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{9943209E-1744-4944-B1BA-4F87FC1A0EEB}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{9943209E-1744-4944-B1BA-4F87FC1A0EEB}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{134D4164-B291-4E19-99B9-E4FA3AFAB62C}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{134D4164-B291-4E19-99B9-E4FA3AFAB62C}"
|
||||||
@@ -29,8 +27,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "infrastructure", "infrastru
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "presentation", "presentation", "{E3C758DC-914D-4B7E-8457-0813F1FDB0CB}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "presentation", "presentation", "{E3C758DC-914D-4B7E-8457-0813F1FDB0CB}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Terminal", "EnvelopeGenerator.Terminal\EnvelopeGenerator.Terminal.csproj", "{A9F9B431-BB9B-49B8-9E2C-0703634A653A}"
|
|
||||||
EndProject
|
|
||||||
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EnvelopeGenerator.Form", "EnvelopeGenerator.Form\EnvelopeGenerator.Form.vbproj", "{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C}"
|
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EnvelopeGenerator.Form", "EnvelopeGenerator.Form\EnvelopeGenerator.Form.vbproj", "{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.PdfEditor", "EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj", "{211619F5-AE25-4BA5-A552-BACAFE0632D3}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.PdfEditor", "EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj", "{211619F5-AE25-4BA5-A552-BACAFE0632D3}"
|
||||||
@@ -41,6 +37,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Jobs", "E
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.WorkerService", "EnvelopeGenerator.WorkerService\EnvelopeGenerator.WorkerService.csproj", "{E3676510-7030-4E85-86E1-51E483E2A3B6}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.WorkerService", "EnvelopeGenerator.WorkerService\EnvelopeGenerator.WorkerService.csproj", "{E3676510-7030-4E85-86E1-51E483E2A3B6}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.API", "EnvelopeGenerator.API\EnvelopeGenerator.API.csproj", "{EC768913-6270-14F4-1DD3-69C87A659462}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -75,14 +73,6 @@ Global
|
|||||||
{5A9984F8-51A2-4558-A415-EC5FEED7CF7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{5A9984F8-51A2-4558-A415-EC5FEED7CF7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{5A9984F8-51A2-4558-A415-EC5FEED7CF7D}.Release|Any CPU.ActiveCfg = Debug|Any CPU
|
{5A9984F8-51A2-4558-A415-EC5FEED7CF7D}.Release|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{5A9984F8-51A2-4558-A415-EC5FEED7CF7D}.Release|Any CPU.Build.0 = Debug|Any CPU
|
{5A9984F8-51A2-4558-A415-EC5FEED7CF7D}.Release|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{E5E12BA4-60C1-48BA-9053-0F8B62B38124}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{E5E12BA4-60C1-48BA-9053-0F8B62B38124}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{E5E12BA4-60C1-48BA-9053-0F8B62B38124}.Release|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{E5E12BA4-60C1-48BA-9053-0F8B62B38124}.Release|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{A9F9B431-BB9B-49B8-9E2C-0703634A653A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{A9F9B431-BB9B-49B8-9E2C-0703634A653A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{A9F9B431-BB9B-49B8-9E2C-0703634A653A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{A9F9B431-BB9B-49B8-9E2C-0703634A653A}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@@ -103,6 +93,10 @@ Global
|
|||||||
{E3676510-7030-4E85-86E1-51E483E2A3B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{E3676510-7030-4E85-86E1-51E483E2A3B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{E3676510-7030-4E85-86E1-51E483E2A3B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{E3676510-7030-4E85-86E1-51E483E2A3B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{E3676510-7030-4E85-86E1-51E483E2A3B6}.Release|Any CPU.Build.0 = Release|Any CPU
|
{E3676510-7030-4E85-86E1-51E483E2A3B6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{EC768913-6270-14F4-1DD3-69C87A659462}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{EC768913-6270-14F4-1DD3-69C87A659462}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{EC768913-6270-14F4-1DD3-69C87A659462}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{EC768913-6270-14F4-1DD3-69C87A659462}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -115,16 +109,15 @@ Global
|
|||||||
{4F32A98D-E6F0-4A09-BD97-1CF26107E837} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
|
{4F32A98D-E6F0-4A09-BD97-1CF26107E837} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
|
||||||
{63E32615-0ECA-42DC-96E3-91037324B7C7} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
{63E32615-0ECA-42DC-96E3-91037324B7C7} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||||
{5A9984F8-51A2-4558-A415-EC5FEED7CF7D} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
|
{5A9984F8-51A2-4558-A415-EC5FEED7CF7D} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
|
||||||
{E5E12BA4-60C1-48BA-9053-0F8B62B38124} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
|
|
||||||
{9943209E-1744-4944-B1BA-4F87FC1A0EEB} = {134D4164-B291-4E19-99B9-E4FA3AFAB62C}
|
{9943209E-1744-4944-B1BA-4F87FC1A0EEB} = {134D4164-B291-4E19-99B9-E4FA3AFAB62C}
|
||||||
{02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {134D4164-B291-4E19-99B9-E4FA3AFAB62C}
|
{02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {134D4164-B291-4E19-99B9-E4FA3AFAB62C}
|
||||||
{E3C758DC-914D-4B7E-8457-0813F1FDB0CB} = {134D4164-B291-4E19-99B9-E4FA3AFAB62C}
|
{E3C758DC-914D-4B7E-8457-0813F1FDB0CB} = {134D4164-B291-4E19-99B9-E4FA3AFAB62C}
|
||||||
{A9F9B431-BB9B-49B8-9E2C-0703634A653A} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
|
|
||||||
{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
|
{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
|
||||||
{211619F5-AE25-4BA5-A552-BACAFE0632D3} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
|
{211619F5-AE25-4BA5-A552-BACAFE0632D3} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
|
||||||
{224C4845-1CDE-22B7-F3A9-1FF9297F70E8} = {0CBC2432-A561-4440-89BC-671B66A24146}
|
{224C4845-1CDE-22B7-F3A9-1FF9297F70E8} = {0CBC2432-A561-4440-89BC-671B66A24146}
|
||||||
{3D0514EA-2681-4B13-AD71-35CC6363DBD7} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
|
{3D0514EA-2681-4B13-AD71-35CC6363DBD7} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
|
||||||
{E3676510-7030-4E85-86E1-51E483E2A3B6} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
|
{E3676510-7030-4E85-86E1-51E483E2A3B6} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
|
||||||
|
{EC768913-6270-14F4-1DD3-69C87A659462} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {73E60370-756D-45AD-A19A-C40A02DACCC7}
|
SolutionGuid = {73E60370-756D-45AD-A19A-C40A02DACCC7}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user