Compare commits

...

26 Commits

Author SHA1 Message Date
eb96842122 Refactor: Rename EmailTemplateQuery to EmailTemplateQueryBase
Refactored the EmailTemplateQuery record to EmailTemplateQueryBase across the codebase. Updated all references, method signatures, inheritance, and documentation to use the new base type. No functional changes; this improves clarity and generalization for email template queries.
2026-02-06 15:19:33 +01:00
a8cb8f935c No changes detected in this commit
No code was added or removed in this commit. The diff does not reflect any modifications to the source files.
2026-02-06 15:18:21 +01:00
387456659d Remove Obsolete attribute from Handle method
The [Obsolete("Use IRepository")] attribute was removed from the Handle method in ReadEmailTemplateQueryHandler, eliminating related compiler warnings and indicating that the method is no longer considered obsolete.
2026-02-06 15:15:46 +01:00
17e3a67fe5 Refactor handler to use generic IRepository for templates
Replaces IEmailTemplateRepository with IRepository<EmailTemplate> in ReadEmailTemplateQueryHandler. Updates method logic to use LINQ queries and FirstOrDefaultAsync. Cleans up obsolete attributes, comments, and using directives. Renames cancellation token parameter for consistency.
2026-02-06 15:14:46 +01:00
632723de5a Refactor email template DTOs and response handling
Replaced ReadEmailTemplateResponse with EmailTemplateDto as the standard DTO for email templates. Updated ReadEmailTemplateQuery and its handler to use EmailTemplateDto. Removed the obsolete MappingProfile and cleaned up EmailTemplateDto with improved documentation. Simplified UpdateEmailTemplateCommandHandler by removing conditional updates for Body and Subject. These changes streamline DTO usage and reduce redundancy in email template handling.
2026-02-06 15:01:04 +01:00
6611b92fdb Update EmailTemplateDto: add timestamps, make fields nullable
Removed ApiExplorerSettings attribute. Changed Body and Subject to nullable strings with init accessors and updated their documentation. Added AddedWhen and ChangedWhen timestamp properties to track template creation and modification dates. Cleaned up property formatting.
2026-02-06 14:14:31 +01:00
5baa28bac8 Move handler to ReadEmailTemplateQuery.cs, remove old file
Refactored by moving ReadEmailTemplateQueryHandler and its implementation into ReadEmailTemplateQuery.cs. Updated using directives accordingly. Deleted the now-redundant ReadEmailTemplateQueryHandler.cs file. No logic changes were made. This consolidates related query and handler code for better maintainability.
2026-02-06 13:53:51 +01:00
c965975f82 Refactor EmailTemplateController to use primary constructor
- Enforce [Authorize(Policy = AuthPolicy.Sender)] on controller
- Switch to primary constructor for dependency injection
- Remove obsolete constructor and private fields
- Update method logic to use constructor parameters directly
- Improve XML documentation and code clarity
- Ensure consistent use of MediatR for command/query handling
2026-02-06 13:51:50 +01:00
d480dd3a36 Refactor DocumentController for async policy-based auth
Refactored DocumentController to use IAuthorizationService and async policy checks via IsUserInPolicyAsync instead of role checks. Implemented IAuthController interface and removed ILogger dependency. Updated usings for new authorization logic.
2026-02-06 13:46:18 +01:00
ae7f0b80f3 Refactor AuthController for interface and policy checks
Refactored AuthController to implement IAuthController and expose AuthService. Removed the protected IsUserInPolicyAsync method in favor of using an extension method for policy checks. Updated the Logout logic to use the new approach. Consolidated using directives into a single line.
2026-02-06 13:41:45 +01:00
ef7c9c2b97 Add IAuthController interface and policy check extension
Introduced IAuthController with AuthService and User properties to standardize authentication handling. Added AuthorizationControllerExtensions with IsUserInPolicyAsync to simplify policy-based authorization checks. Included necessary using directives.
2026-02-06 13:39:14 +01:00
27f0aae8e0 Remove unused AuthExtensions.cs and its extension method
Deleted the AuthExtensions.cs file, which included the AuthorizePolicyAsync extension for IAuthorizationService. This method and related code were no longer needed.
2026-02-06 13:23:17 +01:00
1b10162c85 Refactor AuthController policy checks and response types
Introduce IsUserInPolicyAsync for cleaner policy checks in AuthController and update Logout to use it. Adjust Logout's response type to void and improve documentation and formatting.
2026-02-06 13:23:08 +01:00
bd0426dbee Refactor AuthController for improved policy-based auth
- Inject IAuthorizationService for flexible policy checks
- Replace role checks in Logout with async policy authorization
- Merge IsAuthenticated into Check endpoint with optional role
- Update Check response type and clean up imports
2026-02-06 13:04:57 +01:00
b1551537c8 Add ReceiverOrReceiverTFA constant to AuthPolicy
Added the ReceiverOrReceiverTFA constant to the AuthPolicy class in EnvelopeGenerator.Domain.Constants. This constant is defined as nameof(ReceiverOrReceiverTFA) + nameof(AuthPolicy).
2026-02-06 13:03:02 +01:00
95b2ab5aed Add AuthExtensions with AuthorizePolicyAsync method
Introduced a static AuthExtensions class providing an AuthorizePolicyAsync extension method for IAuthorizationService. This method streamlines policy-based authorization checks by returning a boolean result for a given user and policy name.
2026-02-06 11:50:06 +01:00
ebed51b46a Refactor receiver roles: rename FullyAuth/PreAuth for clarity
Renamed receiver roles FullyAuth → Receiver.Full and PreAuth → Receiver.TFA across the codebase for improved clarity and consistency. Updated all usages, [Authorize] attributes, role checks, authentication logic, and authorization policies to use the new role names. Marked old constants as obsolete and pointed them to the new values. This change enhances code readability and groups receiver roles under the Receiver static class.
2026-02-06 10:49:28 +01:00
0d2425c9cf Refactor to use named authorization policies in controllers
Replaced direct role-based [Authorize] attributes with named
authorization policies (e.g., AuthPolicy.Receiver,
AuthPolicy.SenderOrReceiver) in AnnotationController,
DocumentController, and ReadOnlyController. Added and registered
new policies in Program.cs and updated AuthPolicy constants.
This centralizes and simplifies authorization management.
2026-02-03 16:20:26 +01:00
c6c8747d23 Add ReceiverTFA constant to AuthPolicy class
Added the ReceiverTFA constant to the AuthPolicy class in the EnvelopeGenerator.Domain.Constants namespace. This new constant can be used to represent authentication policies specific to two-factor authentication for receivers.
2026-02-03 16:10:14 +01:00
eb345a0e4d Relax and rename auth policies for sender/receiver roles
Replaced SenderOrReceiverFullyAuth and ReceiverFullyAuth policies with more general SenderOrReceiver and Receiver policies. Updated policy definitions in AuthPolicy.cs to use nameof for clarity. Adjusted AddAuthorizationBuilder configuration and [Authorize] attributes in controllers to use the new, less restrictive policies, simplifying authorization logic.
2026-02-03 16:08:15 +01:00
1b95b9d7e0 Refactor authorization policy naming to AuthPolicy
Renamed AuthorizationPolicies to AuthPolicy and updated all references to use the new naming convention for authorization policy constants. This improves consistency and clarity across the codebase.
2026-02-03 16:01:28 +01:00
d99193979f Update to AddAuthorizationBuilder for policy config
Switched from AddAuthorization to AddAuthorizationBuilder for
defining authorization policies, resulting in more concise and
modern code. Policy logic and requirements remain unchanged.
2026-02-03 15:21:48 +01:00
8742ea6025 Switch to policy-based authorization for controllers
Replaced role-based [Authorize] attributes with policy-based ones in AuthController and TfaRegistrationController. This centralizes authorization logic and allows for more flexible access control.
2026-02-03 15:16:30 +01:00
2b8edc697a Add custom authorization policies and minor Swagger fix
Introduce SenderOrReceiverFullyAuth and ReceiverFullyAuth policies for role-based authorization. Register these policies in Program.cs. Also, fix OpenApiReference type for Swagger security configuration.
2026-02-03 15:15:04 +01:00
7c88d4ed4b Update Sender role constant to "EGSender"
Changed the value of the Sender constant in the Role class from "Sender" to "EGSender" to ensure consistency with updated naming conventions.
2026-02-03 14:54:10 +01:00
a6be907307 Refactor AuthController roles and add check endpoint
- Change AuthController to use IOptions<AuthTokenKeys> for config
- Restrict Logout and new Check endpoints to Sender and Receiver.FullyAuth roles
- Update Logout logic to handle cookie deletion or sign-out based on user role
- Add GET /api/auth/check to verify user role via query param
- Add necessary using statements for new dependencies
2026-02-03 14:54:02 +01:00
25 changed files with 236 additions and 232 deletions

View File

@@ -18,7 +18,7 @@ namespace EnvelopeGenerator.API.Controllers;
/// <summary>
/// Manages annotations and signature lifecycle for envelopes.
/// </summary>
[Authorize(Roles = Role.Receiver.FullyAuth)]
[Authorize(Policy = AuthPolicy.Receiver)]
[ApiController]
[Route("api/[controller]")]
public class AnnotationController : ControllerBase
@@ -54,7 +54,7 @@ public class AnnotationController : ControllerBase
/// </summary>
/// <param name="psPdfKitAnnotation">Annotation payload.</param>
/// <param name="cancel">Cancellation token.</param>
[Authorize(Roles = Role.Receiver.FullyAuth)]
[Authorize(Policy = AuthPolicy.Receiver)]
[HttpPost]
[Obsolete("PSPDF Kit will no longer be used.")]
public async Task<IActionResult> CreateOrUpdate([FromBody] PsPdfKitAnnotation? psPdfKitAnnotation = null, CancellationToken cancel = default)
@@ -87,7 +87,7 @@ public class AnnotationController : ControllerBase
/// Rejects the document for the current receiver.
/// </summary>
/// <param name="reason">Optional rejection reason.</param>
[Authorize(Roles = Role.Receiver.FullyAuth)]
[Authorize(Policy = AuthPolicy.Receiver)]
[HttpPost("reject")]
[Obsolete("Use MediatR")]
public async Task<IActionResult> Reject([FromBody] string? reason = null)

View File

@@ -1,19 +1,27 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using EnvelopeGenerator.API.Controllers.Interfaces;
using EnvelopeGenerator.API.Models;
using EnvelopeGenerator.Domain.Constants;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
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
public partial class AuthController(IOptions<AuthTokenKeys> authTokenKeyOptions, IAuthorizationService authService) : ControllerBase, IAuthController
{
private readonly AuthTokenKeys authTokenKeys = authTokenKeyOptions.Value;
/// <summary>
///
/// </summary>
public IAuthorizationService AuthService { get; } = authService;
/// <summary>
/// Entfernt das Authentifizierungs-Cookie des Benutzers (AuthCookie)
@@ -29,13 +37,19 @@ public partial class AuthController(ILogger<AuthController> logger) : Controller
/// </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.Status200OK)]
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
[Authorize]
[Authorize(Policy = AuthPolicy.SenderOrReceiver)]
[HttpPost("logout")]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
if (await this.IsUserInPolicyAsync(AuthPolicy.Sender))
Response.Cookies.Delete(authTokenKeys.Cookie);
else if (await this.IsUserInPolicyAsync(AuthPolicy.ReceiverOrReceiverTFA))
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
else
return Unauthorized();
return Ok();
}
@@ -51,9 +65,12 @@ public partial class AuthController(ILogger<AuthController> logger) : Controller
/// </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.Status200OK)]
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
[HttpGet("check")]
[Authorize]
[HttpGet]
public IActionResult IsAuthenticated() => Ok();
public IActionResult Check(string? role = null)
=> role is not null && !User.IsInRole(role)
? Unauthorized()
: Ok();
}

View File

@@ -1,3 +1,4 @@
using EnvelopeGenerator.API.Controllers.Interfaces;
using EnvelopeGenerator.API.Extensions;
using EnvelopeGenerator.Application.Documents.Queries;
using EnvelopeGenerator.Domain.Constants;
@@ -16,19 +17,24 @@ namespace EnvelopeGenerator.API.Controllers;
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class DocumentController(IMediator mediator, ILogger<DocumentController> logger) : ControllerBase
public class DocumentController(IMediator mediator, IAuthorizationService authService) : ControllerBase, IAuthController
{
/// <summary>
///
/// </summary>
public IAuthorizationService AuthService => authService;
/// <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}")]
[Authorize(Policy = AuthPolicy.SenderOrReceiver)]
public async Task<IActionResult> GetDocument(CancellationToken cancel, [FromQuery] ReadDocumentQuery? query = null)
{
// Sender: expects query with envelope key
if (User.IsInRole(Role.Sender))
if (await this.IsUserInPolicyAsync(AuthPolicy.Sender))
{
if (query is null)
return BadRequest("Missing document query.");
@@ -40,7 +46,7 @@ public class DocumentController(IMediator mediator, ILogger<DocumentController>
}
// Receiver: resolve envelope id from claims
if (User.IsInRole(Role.Receiver.FullyAuth))
if (await this.IsUserInPolicyAsync(AuthPolicy.Receiver))
{
if (query is not null)
return BadRequest("Query parameters are not allowed for receiver role.");

View File

@@ -11,6 +11,7 @@ using EnvelopeGenerator.Application.Common.Dto;
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using EnvelopeGenerator.Domain.Constants;
namespace EnvelopeGenerator.API.Controllers;
@@ -18,32 +19,18 @@ namespace EnvelopeGenerator.API.Controllers;
/// Controller for managing temp templates.
/// Steuerung zur Verwaltung von E-Mail-Vorlagen.
/// </summary>
/// <remarks>
/// Initialisiert eine neue Instanz der <see cref="EmailTemplateController"/>-Klasse.
/// </remarks>
/// <param name="mapper">
/// <param name="repository">
/// Die AutoMapper-Instanz, die zum Zuordnen von Objekten verwendet wird.
/// </param>
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class EmailTemplateController : ControllerBase
[Authorize(Policy = AuthPolicy.Sender)]
public class EmailTemplateController(IMapper mapper, IRepository<EmailTemplate> repository, IMediator mediator) : ControllerBase
{
private readonly IMapper _mapper;
private readonly IRepository<EmailTemplate> _repository;
private readonly IMediator _mediator;
/// <summary>
/// Initialisiert eine neue Instanz der <see cref="EmailTemplateController"/>-Klasse.
/// </summary>
/// <param name="mapper">
/// <param name="repository">
/// Die AutoMapper-Instanz, die zum Zuordnen von Objekten verwendet wird.
/// </param>
[Obsolete("Use MediatR")]
public EmailTemplateController(IMapper mapper, IRepository<EmailTemplate> repository, IMediator mediator)
{
_mapper = mapper;
_repository = repository;
_mediator = mediator;
}
/// <summary>
/// Ruft E-Mail-Vorlagen basierend auf der angegebenen Abfrage ab.
/// Gibt alles zurück, wenn keine Id- oder Typ-Informationen eingegeben wurden.
@@ -63,12 +50,12 @@ public class EmailTemplateController : ControllerBase
{
if (emailTemplate is null || (emailTemplate.Id is null && emailTemplate.Type is null))
{
var temps = await _repository.Query.ToListAsync();
return Ok(_mapper.Map<IEnumerable<EmailTemplateDto>>(temps));
var temps = await repository.Query.ToListAsync();
return Ok(mapper.Map<IEnumerable<EmailTemplateDto>>(temps));
}
else
{
var temp = await _mediator.Send(emailTemplate);
var temp = await mediator.Send(emailTemplate);
return temp is null ? NotFound() : Ok(temp);
}
}
@@ -95,11 +82,11 @@ public class EmailTemplateController : ControllerBase
/// <response code="401">Wenn der Benutzer nicht authentifiziert ist.</response>
/// <response code="404">Wenn die gesuchte Abfrage nicht gefunden wird.</response>
[HttpPut]
public async Task<IActionResult> Update([FromQuery] EmailTemplateQuery? temp = null, [FromBody] UpdateEmailTemplateCommand? update = null)
public async Task<IActionResult> Update([FromQuery] EmailTemplateQueryBase? temp = null, [FromBody] UpdateEmailTemplateCommand? update = null)
{
if (update is null)
{
await _mediator.Send(new ResetEmailTemplateCommand(temp));
await mediator.Send(new ResetEmailTemplateCommand(temp));
return Ok();
}
else if (temp is null)
@@ -109,7 +96,7 @@ public class EmailTemplateController : ControllerBase
else
{
update.EmailTemplateQuery = temp;
await _mediator.Send(update);
await mediator.Send(update);
return Ok();
}
}

View File

@@ -0,0 +1,38 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
namespace EnvelopeGenerator.API.Controllers.Interfaces;
/// <summary>
///
/// </summary>
public interface IAuthController
{
/// <summary>
///
/// </summary>
IAuthorizationService AuthService { get; }
/// <summary>
///
/// </summary>
ClaimsPrincipal User { get; }
}
/// <summary>
///
/// </summary>
public static class AuthControllerExtensions
{
/// <summary>
///
/// </summary>
/// <param name="controller"></param>
/// <param name="policyName"></param>
/// <returns></returns>
public static async Task<bool> IsUserInPolicyAsync(this IAuthController controller, string policyName)
{
var result = await controller.AuthService.AuthorizeAsync(controller.User, policyName);
return result.Succeeded;
}
}

View File

@@ -37,7 +37,7 @@ public class ReadOnlyController : ControllerBase
/// </summary>
/// <param name="createDto">Creation payload.</param>
[HttpPost]
[Authorize(Roles = Role.Receiver.FullyAuth)]
[Authorize(Policy = AuthPolicy.Receiver)]
public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto)
{
var authReceiverMail = User.GetReceiverMailOfReceiver();

View File

@@ -111,7 +111,7 @@ public class TfaRegistrationController : ControllerBase
/// <summary>
/// Logs out the envelope receiver from cookie authentication.
/// </summary>
[Authorize(Roles = Role.FullyAuth)]
[Authorize(Policy = AuthPolicy.Receiver)]
[HttpPost("auth/logout")]
public async Task<IActionResult> LogOutAsync()
{

View File

@@ -1,6 +1,7 @@
using DigitalData.Core.API;
using DigitalData.Core.Application;
using EnvelopeGenerator.Infrastructure;
using EnvelopeGenerator.Domain.Constants;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Localization;
using Microsoft.EntityFrameworkCore;
@@ -93,7 +94,7 @@ try
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
@@ -176,6 +177,16 @@ try
options.SlidingExpiration = true;
});
builder.Services.AddAuthorizationBuilder()
.AddPolicy(AuthPolicy.SenderOrReceiver, policy =>
policy.RequireRole(Role.Sender, Role.Receiver.Full))
.AddPolicy(AuthPolicy.Sender, policy =>
policy.RequireRole(Role.Sender))
.AddPolicy(AuthPolicy.Receiver, policy =>
policy.RequireRole(Role.Receiver.Full))
.AddPolicy(AuthPolicy.ReceiverTFA, policy =>
policy.RequireRole(Role.Receiver.TFA));
// User manager
#pragma warning disable CS0618 // Type or member is obsolete
builder.Services.AddUserManager<EGDbContext>();

View File

@@ -1,31 +1,37 @@
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.Common.Dto;
namespace EnvelopeGenerator.Application.Common.Dto
/// <summary>
///
/// </summary>
public record EmailTemplateDto
{
/// <summary>
///
/// </summary>
[ApiExplorerSettings(IgnoreApi = true)]
public record EmailTemplateDto
{
/// <summary>
///
/// </summary>
public int Id{ get; init; }
public int Id { get; init; }
/// <summary>
///
/// </summary>
public required string Name { get; init; }
/// <summary>
///
/// </summary>
public required string Name { get; init; }
/// <summary>
///
/// </summary>
public required string Body { get; set; }
/// <summary>
/// Das Datum und die Uhrzeit, wann die Vorlage hinzugefügt wurde.
/// </summary>
public DateTime AddedWhen { get; init; }
/// <summary>
///
/// </summary>
public required string Subject { get; set; }
};
}
/// <summary>
/// Der Inhalt (Body) der E-Mail-Vorlage. Kann null sein.
/// </summary>
public string? Body { get; init; }
/// <summary>
/// Der Betreff der E-Mail-Vorlage. Kann null sein.
/// </summary>
public string? Subject { get; init; }
/// <summary>
/// Das Datum und die Uhrzeit, wann die Vorlage zuletzt geändert wurde. Kann null sein.
/// </summary>
public DateTime? ChangedWhen { get; init; }
};

View File

@@ -6,7 +6,7 @@ namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset;
/// <summary>
/// Ein Befehl zum Zurücksetzen einer E-Mail-Vorlage auf die Standardwerte.
/// Erbt von <see cref="EmailTemplateQuery"/> und ermöglicht die Angabe einer optionalen ID und eines Typs der E-Mail-Vorlage.<br/><br/>
/// Erbt von <see cref="EmailTemplateQueryBase"/> und ermöglicht die Angabe einer optionalen ID und eines Typs der E-Mail-Vorlage.<br/><br/>
/// Beispiele:<br/>
/// 0 - DocumentReceived: Benachrichtigung über den Empfang eines Dokuments.<br/>
/// 1 - DocumentSigned: Benachrichtigung über die Unterzeichnung eines Dokuments.<br/>
@@ -19,13 +19,13 @@ namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset;
/// 8 - DocumentRejected_REC (Für den ablehnenden Empfänger): Mail an den ablehnenden Empfänger, wenn das Dokument abgelehnt wird.<br/>
/// 9 - DocumentRejected_REC_2 (Für sonstige Empfänger): Mail an andere Empfänger (Brief), wenn das Dokument abgelehnt wird.<br/>
/// </summary>
public record ResetEmailTemplateCommand : EmailTemplateQuery, IRequest
public record ResetEmailTemplateCommand : EmailTemplateQueryBase, IRequest
{
/// <summary>
///
/// </summary>
/// <param name="orginal"></param>
public ResetEmailTemplateCommand(EmailTemplateQuery? orginal = null) : base(orginal ?? new())
public ResetEmailTemplateCommand(EmailTemplateQueryBase? orginal = null) : base(orginal ?? new())
{
}

View File

@@ -19,7 +19,7 @@ public record UpdateEmailTemplateCommand(string? Body = null, string? Subject =
/// Die Abfrage, die die E-Mail-Vorlage darstellt, die aktualisiert werden soll.
/// </param>
[JsonIgnore]
public EmailTemplateQuery? EmailTemplateQuery { get; set; }
public EmailTemplateQueryBase? EmailTemplateQuery { get; set; }
/// <summary>
///

View File

@@ -62,12 +62,6 @@ public class UpdateEmailTemplateCommandHandler : IRequestHandler<UpdateEmailTemp
throw new NotFoundException();
}
if (request.Body is not null)
tempDto.Body = request.Body;
if (request.Subject is not null)
tempDto.Subject = request.Subject;
await _repository.UpdateAsync(tempDto, t => t.Id == tempDto.Id, cancel);
}
}

View File

@@ -19,6 +19,6 @@ namespace EnvelopeGenerator.Application.EmailTemplates;
/// 8 - DocumentRejected_REC (Für den ablehnenden Empfänger): Mail an den ablehnenden Empfänger, wenn das Dokument abgelehnt wird.
/// 9 - DocumentRejected_REC_2 (Für sonstige Empfänger): Mail an andere Empfänger (Brief), wenn das Dokument abgelehnt wird.
/// </param>
public record EmailTemplateQuery(int? Id = null, EmailTemplateType? Type = null)
public record EmailTemplateQueryBase(int? Id = null, EmailTemplateType? Type = null)
{
}

View File

@@ -1,24 +0,0 @@
using AutoMapper;
using EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
using EnvelopeGenerator.Domain.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EnvelopeGenerator.Application.EmailTemplates;
/// <summary>
///
/// </summary>
public class MappingProfile : Profile
{
/// <summary>
///
/// </summary>
public MappingProfile()
{
CreateMap<EmailTemplate, ReadEmailTemplateResponse>();
}
}

View File

@@ -1,12 +0,0 @@
using MediatR;
namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
/// <summary>
/// Stellt eine Abfrage dar, um eine E-Mail-Vorlage zu lesen.
/// Diese Klasse erbt von <see cref="EmailTemplateQuery"/>.
/// </summary>
public record ReadEmailTemplateQuery : EmailTemplateQuery, IRequest<ReadEmailTemplateResponse?>
{
}

View File

@@ -1,52 +0,0 @@
using AutoMapper;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
using EnvelopeGenerator.Domain.Constants;
using MediatR;
namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
/// <summary>
///
/// </summary>
public class ReadEmailTemplateQueryHandler : IRequestHandler<ReadEmailTemplateQuery, ReadEmailTemplateResponse?>
{
private readonly IMapper _mapper;
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
private readonly IEmailTemplateRepository _repository;
/// <summary>
/// Initialisiert eine neue Instanz der <see cref="EmailTemplateController"/>-Klasse.
/// </summary>
/// <param name="mapper">
/// <param name="repository">
/// Die AutoMapper-Instanz, die zum Zuordnen von Objekten verwendet wird.
/// </param>
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
public ReadEmailTemplateQueryHandler(IMapper mapper, IEmailTemplateRepository repository)
{
_mapper = mapper;
_repository = repository;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
[Obsolete("Use IRepository")]
public async Task<ReadEmailTemplateResponse?> Handle(ReadEmailTemplateQuery request, CancellationToken cancellationToken)
{
var temp = request.Id is int id
? await _repository.ReadByIdAsync(id)
: request.Type is EmailTemplateType type
? await _repository.ReadByNameAsync(type)
: throw new InvalidOperationException("Either a valid integer ID or a valid EmailTemplateType must be provided in the request.");
var res = _mapper.Map<ReadEmailTemplateResponse>(temp);
return res;
}
}

View File

@@ -1,37 +0,0 @@
namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
/// <summary>
/// Stellt die Antwort für eine Abfrage von E-Mail-Vorlagen bereit.
/// </summary>
public class ReadEmailTemplateResponse
{
/// <summary>
/// Die eindeutige Kennung der E-Mail-Vorlage.
/// </summary>
public int Id { get; set; }
/// <summary>
/// Name des Typs
/// </summary>
public required string Name { get; set; }
/// <summary>
/// Das Datum und die Uhrzeit, wann die Vorlage hinzugefügt wurde.
/// </summary>
public DateTime AddedWhen { get; set; }
/// <summary>
/// Der Inhalt (Body) der E-Mail-Vorlage. Kann null sein.
/// </summary>
public string? Body { get; set; }
/// <summary>
/// Der Betreff der E-Mail-Vorlage. Kann null sein.
/// </summary>
public string? Subject { get; set; }
/// <summary>
/// Das Datum und die Uhrzeit, wann die Vorlage zuletzt geändert wurde. Kann null sein.
/// </summary>
public DateTime? ChangedWhen { get; set; }
}

View File

@@ -0,0 +1,55 @@
using AutoMapper;
using MediatR;
using EnvelopeGenerator.Application.Common.Dto;
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
/// <summary>
/// Stellt eine Abfrage dar, um eine E-Mail-Vorlage zu lesen.
/// Diese Klasse erbt von <see cref="EmailTemplateQueryBase"/>.
/// </summary>
public record ReadEmailTemplateQuery : EmailTemplateQueryBase, IRequest<EmailTemplateDto?>
{
}
/// <summary>
///
/// </summary>
public class ReadEmailTemplateQueryHandler : IRequestHandler<ReadEmailTemplateQuery, EmailTemplateDto?>
{
private readonly IMapper _mapper;
private readonly IRepository<EmailTemplate> _repo;
/// <summary>
/// Initialisiert eine neue Instanz der <see cref="EmailTemplateController"/>-Klasse.
/// </summary>
/// <param name="mapper">
/// <param name="repo">
/// Die AutoMapper-Instanz, die zum Zuordnen von Objekten verwendet wird.
/// </param>
public ReadEmailTemplateQueryHandler(IMapper mapper, IRepository<EmailTemplate> repo)
{
_mapper = mapper;
_repo = repo;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancel"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public async Task<EmailTemplateDto?> Handle(ReadEmailTemplateQuery request, CancellationToken cancel)
{
var query = request.Id is int id
? _repo.Query.Where(temp => temp.Id == id)
: _repo.Query.Where(temp => temp.Name == request.Type!.ToString());
return _mapper.Map<EmailTemplateDto>(await query.FirstOrDefaultAsync(cancel));
}
}

View File

@@ -0,0 +1,15 @@
namespace EnvelopeGenerator.Domain.Constants
{
public static class AuthPolicy
{
public const string SenderOrReceiver = nameof(SenderOrReceiver) + nameof(AuthPolicy);
public const string ReceiverOrReceiverTFA = nameof(ReceiverOrReceiverTFA) + nameof(AuthPolicy);
public const string Sender = nameof(Sender) + nameof(AuthPolicy);
public const string Receiver = nameof(Receiver) + nameof(AuthPolicy);
public const string ReceiverTFA = nameof(ReceiverTFA) + nameof(AuthPolicy);
}
}

View File

@@ -6,18 +6,18 @@ namespace EnvelopeGenerator.Domain.Constants
{
public static class Role
{
[Obsolete("Use Receiver.PreAuth or Receiver.FullyAuth")]
public const string PreAuth = "PreAuth";
[Obsolete("Use Receiver.TFA")]
public const string ReceiverTFA = Receiver.TFA;
[Obsolete("Use Receiver.PreAuth or Receiver.FullyAuth")]
public const string FullyAuth = "FullyAuth";
[Obsolete("Use Receiver.Full")]
public const string ReceiverFull = Receiver.Full;
public static class Receiver
{
public const string PreAuth = "PreAuth";
public const string FullyAuth = "FullyAuth";
public const string TFA = "EGReceiverTFA";
public const string Full = "EGReceiver";
}
public const string Sender = "Sender";
public const string Sender = "EGSender";
}
}

View File

@@ -15,7 +15,7 @@ using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Web.Controllers;
[Authorize(Roles = Role.FullyAuth)]
[Authorize(Roles = Role.ReceiverFull)]
[ApiController]
[Route("api/[controller]")]
public class AnnotationController : ControllerBase
@@ -42,7 +42,7 @@ public class AnnotationController : ControllerBase
_logger = logger;
}
[Authorize(Roles = Role.FullyAuth)]
[Authorize(Roles = Role.ReceiverFull)]
[HttpPost]
public async Task<IActionResult> CreateOrUpdate([FromBody] PsPdfKitAnnotation? psPdfKitAnnotation = null, CancellationToken cancel = default)
{
@@ -80,7 +80,7 @@ public class AnnotationController : ControllerBase
return Ok();
}
[Authorize(Roles = Role.FullyAuth)]
[Authorize(Roles = Role.ReceiverFull)]
[HttpPost("reject")]
[Obsolete("Use DigitalData.Core.Exceptions and .Middleware")]
public async Task<IActionResult> Reject([FromBody] string? reason = null)

View File

@@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Web.Controllers;
[Authorize(Roles = Role.FullyAuth)]
[Authorize(Roles = Role.ReceiverFull)]
[ApiController]
[Route("api/[controller]")]
public class DocumentController : ControllerBase

View File

@@ -107,7 +107,7 @@ public class EnvelopeController : ViewControllerBase
return this.ViewEnvelopeNotFound();
}
var er_secret = er_secret_res.Data;
await HttpContext.SignInEnvelopeAsync(er_secret, Role.FullyAuth);
await HttpContext.SignInEnvelopeAsync(er_secret, Role.ReceiverFull);
return await CreateShowEnvelopeView(er_secret);
}
#endregion UseAccessCode
@@ -172,7 +172,7 @@ public class EnvelopeController : ViewControllerBase
}
// show envelope if already logged in
if (User.IsInRole(Role.FullyAuth))
if (User.IsInRole(Role.ReceiverFull))
return await CreateShowEnvelopeView(er_secret);
if (auth.HasMulti)
@@ -206,7 +206,7 @@ public class EnvelopeController : ViewControllerBase
.WithData("ErrorMessage", _localizer.WrongEnvelopeReceiverId());
}
await HttpContext.SignInEnvelopeAsync(er_secret, Role.FullyAuth);
await HttpContext.SignInEnvelopeAsync(er_secret, Role.ReceiverFull);
return await CreateShowEnvelopeView(er_secret);
}
@@ -225,9 +225,9 @@ public class EnvelopeController : ViewControllerBase
&& uuidClaim == er.Envelope?.Uuid
&& signatureClaim is not null
&& signatureClaim == er.Receiver?.Signature
&& User.IsInRole(Role.FullyAuth))
&& User.IsInRole(Role.ReceiverFull))
{
await HttpContext.SignInEnvelopeAsync(er, Role.FullyAuth);
await HttpContext.SignInEnvelopeAsync(er, Role.ReceiverFull);
//add PSPDFKit licence key
ViewData["PSPDFKitLicenseKey"] = _configuration["PSPDFKitLicenseKey"];
@@ -262,7 +262,7 @@ public class EnvelopeController : ViewControllerBase
return this.ViewDocumentNotFound();
}
await HttpContext.SignInEnvelopeAsync(er, Role.FullyAuth);
await HttpContext.SignInEnvelopeAsync(er, Role.ReceiverFull);
ViewData["ReadAndConfirm"] = er.Envelope.ReadOnly;
@@ -334,7 +334,7 @@ public class EnvelopeController : ViewControllerBase
await _rcvService.UpdateAsync(rcv);
}
await HttpContext.SignInEnvelopeAsync(er_secret, Role.PreAuth);
await HttpContext.SignInEnvelopeAsync(er_secret, Role.ReceiverTFA);
return await TFAViewAsync(auth.UserSelectSMS, er_secret, envelopeReceiverId);
}
@@ -348,7 +348,7 @@ public class EnvelopeController : ViewControllerBase
if (er_secret.Receiver!.TotpSecretkey is null)
throw new InvalidOperationException($"TotpSecretkey of DTO cannot validate without TotpSecretkey. Dto: {JsonConvert.SerializeObject(er_secret)}");
if (!User.IsInRole(Role.PreAuth) || !_envSmsHandler.VerifyTotp(auth.SmsCode!, er_secret.Receiver.TotpSecretkey))
if (!User.IsInRole(Role.ReceiverTFA) || !_envSmsHandler.VerifyTotp(auth.SmsCode!, er_secret.Receiver.TotpSecretkey))
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
ViewData["ErrorMessage"] = _localizer.WrongAccessCode();
@@ -364,7 +364,7 @@ public class EnvelopeController : ViewControllerBase
if (er_secret.Receiver!.TotpSecretkey is null)
throw new InvalidOperationException($"TotpSecretkey of DTO cannot validate without TotpSecretkey. Dto: {JsonConvert.SerializeObject(er_secret)}");
if (!User.IsInRole(Role.PreAuth) || !_authenticator.VerifyTotp(auth.AuthenticatorCode!, er_secret.Receiver.TotpSecretkey, window: VerificationWindow.RfcSpecifiedNetworkDelay))
if (!User.IsInRole(Role.ReceiverTFA) || !_authenticator.VerifyTotp(auth.AuthenticatorCode!, er_secret.Receiver.TotpSecretkey, window: VerificationWindow.RfcSpecifiedNetworkDelay))
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
ViewData["ErrorMessage"] = _localizer.WrongAccessCode();

View File

@@ -34,7 +34,7 @@ namespace EnvelopeGenerator.Web.Controllers
}
[HttpPost]
[Authorize(Roles = Role.FullyAuth)]
[Authorize(Roles = Role.ReceiverFull)]
[Obsolete("Use MediatR")]
public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto)
{

View File

@@ -91,7 +91,7 @@ public class TFARegController : ViewControllerBase
}
}
[Authorize(Roles = Role.FullyAuth)]
[Authorize(Roles = Role.ReceiverFull)]
[HttpPost("auth/logout")]
public async Task<IActionResult> LogOut()
{