Refaktorisierung: Absicherung von DB-Operationen und Verbesserung der Geschäftslogik

- Implementierung von LINQ-Abfragen innerhalb der Core-Bibliothek zur Minderung von SQL-Injection-Anfälligkeiten für DB-Operationen von Umschlägen und Empfängern.
- Aktualisierung der Geschäftslogik in der Service-Schicht für verbessertes Transaktionshandling.
- Erweiterung der ServiceMessage um eine neue Flag-Funktion zum Verfolgen von Cybersecurity- und Datenintegritätsproblemen.
- Hinzufügen spezifischer Benutzerverhaltensflags zur besseren Erkennung und Behandlung potenzieller Datenverletzungen.
This commit is contained in:
Developer 02
2024-04-24 13:45:03 +02:00
parent f2e718565d
commit 6338b81571
47 changed files with 644 additions and 310 deletions

View File

@@ -5,13 +5,76 @@
/// </summary>
public static class EnvelopeGeneratorExtensions
{
/// <summary>
/// Validates whether a given string is a correctly formatted Base-64 encoded string.
/// </summary>
/// <remarks>
/// This method checks the string for proper Base-64 formatting, which includes validating
/// the length of the string (must be divisible by 4). It also checks each character to ensure
/// it belongs to the Base-64 character set (A-Z, a-z, 0-9, '+', '/', and '=' for padding).
/// The method ensures that padding characters ('=') only appear at the end of the string and
/// are in a valid configuration (either one '=' at the end if the string's length % 4 is 3,
/// or two '==' if the length % 4 is 2).
/// </remarks>
/// <param name="input">The Base-64 encoded string to validate.</param>
/// <returns>
/// <c>true</c> if the string is a valid Base-64 encoded string; otherwise, <c>false</c>.
/// </returns>
/// <example>
/// <code>
/// string testString = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnk=";
/// bool isValid = IsValidBase64String(testString);
/// Console.WriteLine(isValid); // Output: true
/// </code>
/// </example>
public static bool IsBase64String(this string input)
{
// Check if the string is null or empty
if (string.IsNullOrEmpty(input))
{
return false;
}
// Replace valid base-64 padding
input = input.Trim();
int mod4 = input.Length % 4;
if (mod4 > 0)
{
// Base-64 string lengths should be divisible by 4
return false;
}
// Check each character to ensure it is valid base-64
foreach (char c in input)
{
if (!char.IsLetterOrDigit(c) && c != '+' && c != '/' && c != '=')
{
// Invalid character detected
return false;
}
}
// Ensure no invalid padding scenarios exist
if (input.EndsWith("==") && (input.Length % 4 == 0) ||
input.EndsWith("=") && (input.Length % 4 == 3))
{
return true;
}
return input.IndexOf('=') == -1; // No padding allowed except at the end
}
/// <summary>
/// Decodes the envelope receiver ID and extracts the envelope UUID and receiver signature.
/// </summary>
/// <param name="envelopeReceiverId">The base64 encoded string containing the envelope UUID and receiver signature.</param>
/// <returns>A tuple containing the envelope UUID and receiver signature.</returns>
public static (string EnvelopeUuid, string ReceiverSignature) DecodeEnvelopeReceiverId(this string envelopeReceiverId)
public static (string? EnvelopeUuid, string? ReceiverSignature) DecodeEnvelopeReceiverId(this string envelopeReceiverId)
{
if (!envelopeReceiverId.IsBase64String())
{
return (null, null);
}
byte[] bytes = Convert.FromBase64String(envelopeReceiverId);
string decodedString = System.Text.Encoding.UTF8.GetString(bytes);
string[] parts = decodedString.Split(new string[] { "::" }, StringSplitOptions.None);

View File

@@ -5,6 +5,7 @@ using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
using static EnvelopeGenerator.Common.Constants;
namespace EnvelopeGenerator.Application.Services
{
@@ -14,5 +15,9 @@ namespace EnvelopeGenerator.Application.Services
: base(repository, translationService, mapper)
{
}
public async Task<int> CountAsync(int? envelopeId = null, string? userReference = null, int? status = null) => await _repository.CountAsync(envelopeId: envelopeId, userReference: userReference, status: status);
public async Task<bool> AccessCodeAlreadyRequested(int envelopeId, string userReference) => await _repository.CountAsync(envelopeId: envelopeId, userReference:userReference, status: (int) EnvelopeStatus.AccessCodeRequested) > 0;
}
}

View File

@@ -6,7 +6,6 @@ using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Application.Services
{
@@ -17,10 +16,84 @@ namespace EnvelopeGenerator.Application.Services
{
}
public async Task<IServiceMessage> VerifyAccessCode(string envelopeUuid, string accessCode)
public async Task<IServiceResult<IEnumerable<EnvelopeReceiverDto>>> ReadBySignatureAsync(string signature, bool withEnvelope = false, bool withReceiver = true)
{
var envelopeAccessCode = await _repository.ReadAccessCodeByEnvelopeUuid(envelopeUuid);
return CreateMessage(isSuccess: accessCode == envelopeAccessCode) ;
var env_rcvs = await _repository.ReadBySignatureAsync(signature: signature, withEnvelope: withEnvelope, withReceiver: withReceiver);
return Successful(_mapper.MapOrThrow<IEnumerable<EnvelopeReceiverDto>>(env_rcvs));
}
public async Task<IServiceResult<IEnumerable<EnvelopeReceiverDto>>> ReadByUuidAsync(string uuid, bool withEnvelope = true, bool withReceiver = false)
{
var env_rcvs = await _repository.ReadByUuidAsync(uuid: uuid, withEnvelope: withEnvelope, withReceiver: withReceiver);
return Successful(_mapper.MapOrThrow<IEnumerable<EnvelopeReceiverDto>>(env_rcvs));
}
public async Task<IServiceResult<EnvelopeReceiverDto>> ReadByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true)
{
var env_rcv = await _repository.ReadByUuidSignatureAsync(uuid: uuid, signature: signature, withEnvelope: withEnvelope, withReceiver: withReceiver);
if (env_rcv is null)
return Failed<EnvelopeReceiverDto>()
.WithClientMessageKey(MessageKey.EnvelopeReceiverNotFound);
return Successful(_mapper.MapOrThrow<EnvelopeReceiverDto>(env_rcv));
}
public async Task<IServiceResult<EnvelopeReceiverDto>> ReadByEnvelopeReceiverIdAsync(string envelopeReceiverId, bool withEnvelope = true, bool withReceiver = true)
{
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
if (uuid is null || signature is null)
return Failed<EnvelopeReceiverDto>()
.WithClientMessageKey(MessageKey.WrongEnvelopeReceiverId2Client)
.WithWarningMessage((uuid, signature).ToTitle())
.WithWarningMessageKey(MessageKey.WrongEnvelopeReceiverId2Logger)
.WithWarningMessageKey(MessageKey.PossibleSecurityBreach)
.WithFlag(Flag.PossibleSecurityBreach);
return await ReadByUuidSignatureAsync(uuid: uuid, signature: signature, withEnvelope: withEnvelope, withReceiver: withReceiver);
}
public async Task<IServiceResult<bool>> VerifyAccessCodeAsync(string uuid, string signature, string accessCode)
{
var er = await _repository.ReadByUuidSignatureAsync(uuid: uuid, signature: signature);
if (er is null)
return Failed<bool>()
.WithClientMessageKey(MessageKey.EnvelopeOrReceiverNonexists)
.WithWarningMessage((uuid, signature).ToTitle())
.WithWarningMessageKey(MessageKey.EnvelopeOrReceiverNonexists)
.WithWarningMessageKey(MessageKey.PossibleDataIntegrityIssue)
.WithFlag(MessageKey.PossibleDataIntegrityIssue);
var actualAccessCode = er.AccessCode;
if (actualAccessCode is null)
return Failed<bool>()
.WithClientMessageKey(MessageKey.AccessCodeNull2Client)
.WithCriticalMessage((uuid, signature).ToTitle())
.WithCriticalMessageKey(MessageKey.AccessCodeNull2Logger)
.WithCriticalMessageKey(MessageKey.DataIntegrityIssue)
.WithFlag(Flag.DataIntegrityIssue);
else if(accessCode != actualAccessCode)
return Successful(false).WithClientMessageKey(MessageKey.WrongAccessCode);
else
return Successful(true);
}
public async Task<IServiceResult<bool>> VerifyAccessCodeAsync(string envelopeReceiverId, string accessCode)
{
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
if (uuid is null || signature is null)
return Failed<bool>()
.WithClientMessageKey(MessageKey.WrongEnvelopeReceiverId2Client)
.WithCriticalMessageKey(MessageKey.WrongEnvelopeReceiverId2Logger)
.WithCriticalMessageKey(MessageKey.SecurityBreach)
.WithCriticalMessage("Attempt to verify access code detected. Such actions are generally not initiated by well-intentioned users. Potential security breach suspected. Immediate investigation required.")
.WithFlag(Flag.SecurityBreach);
return await VerifyAccessCodeAsync(uuid: uuid, signature: signature, accessCode: accessCode);
}
}
}

View File

@@ -6,26 +6,29 @@ using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
using Microsoft.Extensions.Logging;
namespace EnvelopeGenerator.Application.Services
{
public class EnvelopeService : BasicCRUDService<IEnvelopeRepository, EnvelopeDto, Envelope, int>, IEnvelopeService
{
public EnvelopeService(IEnvelopeRepository repository, IKeyTranslationService translationService, IMapper mapper)
private readonly ILogger _logger;
public EnvelopeService(IEnvelopeRepository repository, IKeyTranslationService translationService, IMapper mapper, ILogger<EnvelopeService> logger)
: base(repository, translationService, mapper)
{
_logger = logger;
}
public async Task<IServiceResult<IEnumerable<EnvelopeDto>>> ReadAllWithAsync(bool documents = false, bool receivers = false, bool history = false, bool documentReceiverElement = false)
public async Task<IServiceResult<IEnumerable<EnvelopeDto>>> ReadAllWithAsync(bool documents = false, bool envelopeReceivers = false, bool history = false, bool documentReceiverElement = false)
{
var envelopes = await _repository.ReadAllWithAsync(documents: documents, receivers: receivers, history: history, documentReceiverElement: documentReceiverElement);
var envelopes = await _repository.ReadAllWithAsync(documents: documents, envelopeReceivers: envelopeReceivers, history: history, documentReceiverElement: documentReceiverElement);
var readDto = _mapper.MapOrThrow<IEnumerable<EnvelopeDto>>(envelopes);
return Successful(readDto);
}
public async Task<IServiceResult<EnvelopeDto>> ReadByUuidAsync(string uuid, string? signature = null, bool withDocuments = false, bool withReceivers = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withAll = false)
public async Task<IServiceResult<EnvelopeDto>> ReadByUuidAsync(string uuid, string? signature = null, bool withDocuments = false, bool withEnvelopeReceivers = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withUser = false, bool withAll = false)
{
var envelope = await _repository.ReadByUuidAsync(uuid: uuid, signature: signature, withDocuments: withDocuments, withReceivers: withReceivers, withHistory: withHistory, withDocumentReceiverElement: withDocumentReceiverElement, withAll:withAll);
var envelope = await _repository.ReadByUuidAsync(uuid: uuid, signature: signature, withDocuments: withDocuments, withEnvelopeReceivers: withEnvelopeReceivers, withHistory: withHistory, withDocumentReceiverElement: withDocumentReceiverElement, withUser:withUser, withAll:withAll);
if (envelope is null)
return Failed<EnvelopeDto>();