diff --git a/EnvelopeGenerator.Application/Contracts/IEnvelopeHistoryService.cs b/EnvelopeGenerator.Application/Contracts/IEnvelopeHistoryService.cs index 9192756f..933037cd 100644 --- a/EnvelopeGenerator.Application/Contracts/IEnvelopeHistoryService.cs +++ b/EnvelopeGenerator.Application/Contracts/IEnvelopeHistoryService.cs @@ -2,10 +2,14 @@ using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; +using static EnvelopeGenerator.Common.Constants; namespace EnvelopeGenerator.Application.Contracts { public interface IEnvelopeHistoryService : IBasicCRUDService { + Task CountAsync(int? envelopeId = null, string? userReference = null, int? status = null); + + Task AccessCodeAlreadyRequested(int envelopeId, string userReference); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Contracts/IEnvelopeReceiverService.cs b/EnvelopeGenerator.Application/Contracts/IEnvelopeReceiverService.cs index 348257e0..58b246ed 100644 --- a/EnvelopeGenerator.Application/Contracts/IEnvelopeReceiverService.cs +++ b/EnvelopeGenerator.Application/Contracts/IEnvelopeReceiverService.cs @@ -7,6 +7,17 @@ namespace EnvelopeGenerator.Application.Contracts { public interface IEnvelopeReceiverService : IBasicCRUDService { - Task VerifyAccessCode(string envelopeUuid, string accessCode); + + Task>> ReadByUuidAsync(string uuid, bool withEnvelope = true, bool withReceiver = false); + + Task>> ReadBySignatureAsync(string signature, bool withEnvelope = false, bool withReceiver = true); + + Task> ReadByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true); + + Task> ReadByEnvelopeReceiverIdAsync(string envelopeReceiverId, bool withEnvelope = true, bool withReceiver = true); + + Task> VerifyAccessCodeAsync(string uuid, string signature, string accessCode); + + Task> VerifyAccessCodeAsync(string envelopeReceiverId, string accessCode); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Contracts/IEnvelopeService.cs b/EnvelopeGenerator.Application/Contracts/IEnvelopeService.cs index 6ce6d9e3..1c275258 100644 --- a/EnvelopeGenerator.Application/Contracts/IEnvelopeService.cs +++ b/EnvelopeGenerator.Application/Contracts/IEnvelopeService.cs @@ -7,8 +7,8 @@ namespace EnvelopeGenerator.Application.Contracts { public interface IEnvelopeService : IBasicCRUDService { - Task>> ReadAllWithAsync(bool documents = false, bool receivers = false, bool history = false, bool documentReceiverElement = false); + Task>> ReadAllWithAsync(bool documents = false, bool envelopeReceivers = false, bool history = false, bool documentReceiverElement = false); - Task> ReadByUuidAsync(string uuid, string? signature = null, bool withDocuments = false, bool withReceivers = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withAll = false); + Task> ReadByUuidAsync(string uuid, string? signature = null, bool withDocuments = false, bool withEnvelopeReceivers = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withUser = false, bool withAll = false); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/DTOs/EnvelopeDto.cs b/EnvelopeGenerator.Application/DTOs/EnvelopeDto.cs index 0a352d7f..7203c1d2 100644 --- a/EnvelopeGenerator.Application/DTOs/EnvelopeDto.cs +++ b/EnvelopeGenerator.Application/DTOs/EnvelopeDto.cs @@ -1,4 +1,5 @@ -using EnvelopeGenerator.Domain.Entities; +using DigitalData.UserManager.Domain.Entities; +using EnvelopeGenerator.Domain.Entities; namespace EnvelopeGenerator.Application.DTOs { @@ -26,13 +27,13 @@ namespace EnvelopeGenerator.Application.DTOs int? ExpiresWhenDays, int? ExpiresWarningWhenDays, bool DmzMoved, - ReceiverDto? User, + User? User, EnvelopeType? EnvelopeType, string? EnvelopeTypeTitle, bool IsAlreadySent, string? StatusTranslated, string? ContractTypeTranslated, IEnumerable? Documents, - IEnumerable? Receivers, + IEnumerable? EnvelopeReceivers, IEnumerable? History); } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/DTOs/EnvelopeReceiverDto.cs b/EnvelopeGenerator.Application/DTOs/EnvelopeReceiverDto.cs index 02404c47..42de4e65 100644 --- a/EnvelopeGenerator.Application/DTOs/EnvelopeReceiverDto.cs +++ b/EnvelopeGenerator.Application/DTOs/EnvelopeReceiverDto.cs @@ -6,10 +6,10 @@ namespace EnvelopeGenerator.Application.DTOs int EnvelopeId, int ReceiverId, int Sequence, - string Name, - string JobTitle, - string CompanyName, - string PrivateMessage, + string? Name, + string? JobTitle, + string? CompanyName, + string? PrivateMessage, DateTime AddedWhen, DateTime? ChangedWhen, Envelope? Envelope, diff --git a/EnvelopeGenerator.Application/DTOs/ReceiverDto.cs b/EnvelopeGenerator.Application/DTOs/ReceiverDto.cs index 8e3a3f96..cc27ef93 100644 --- a/EnvelopeGenerator.Application/DTOs/ReceiverDto.cs +++ b/EnvelopeGenerator.Application/DTOs/ReceiverDto.cs @@ -4,6 +4,6 @@ int Id, string EmailAddress, string Signature, - DateTime AddedWhen, - IEnumerable? EnvelopeReceivers); + DateTime AddedWhen + ); } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/EnvelopeFlag.cs b/EnvelopeGenerator.Application/EnvelopeFlag.cs new file mode 100644 index 00000000..516575ae --- /dev/null +++ b/EnvelopeGenerator.Application/EnvelopeFlag.cs @@ -0,0 +1,7 @@ +namespace EnvelopeGenerator.Application +{ + public enum EnvelopeFlag + { + EnvelopeOrReceiverNonexists + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj index 01eaa890..8607f5a3 100644 --- a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj +++ b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj @@ -22,6 +22,18 @@ ..\..\WebCoreModules\DigitalData.Core.Application\bin\Debug\net7.0\DigitalData.Core.Contracts.dll + + ..\..\WebUserManager\DigitalData.UserManager.Application\bin\Debug\net7.0\DigitalData.Core.Infrastructure.dll + + + ..\..\WebUserManager\DigitalData.UserManager.Application\bin\Debug\net7.0\DigitalData.UserManager.Application.dll + + + ..\..\WebUserManager\DigitalData.UserManager.Domain\bin\Debug\net7.0\DigitalData.UserManager.Domain.dll + + + ..\..\WebUserManager\DigitalData.UserManager.Application\bin\Debug\net7.0\DigitalData.UserManager.Infrastructure.dll + diff --git a/EnvelopeGenerator.Application/EnvelopeGeneratorExtensions.cs b/EnvelopeGenerator.Application/EnvelopeGeneratorExtensions.cs new file mode 100644 index 00000000..6d7ad6f9 --- /dev/null +++ b/EnvelopeGenerator.Application/EnvelopeGeneratorExtensions.cs @@ -0,0 +1,39 @@ +using EnvelopeGenerator.Application.Services; +using Microsoft.Extensions.Logging; +using System.Text; + +namespace EnvelopeGenerator.Application +{ + public static class EnvelopeGeneratorExtensions + { + public static void LogEnvelopeError(this ILogger logger, string receiverId, string? message, params object?[] args) + { + (string? envelopeUuid, string? receiverSignature) = receiverId.DecodeEnvelopeReceiverId(); + + var sb = new StringBuilder($"Envelope Uuid: {envelopeUuid}\nReceiver Signature: {receiverSignature}"); + + if (message is not null) + sb.AppendLine().Append(message); + + logger.Log(LogLevel.Error, sb.ToString(), args); + } + + public static void LogEnvelopeError(this ILogger logger, string uuid, string? signature = null, string? message = null, params object?[] args) + { + var sb = new StringBuilder($"Envelope Uuid: {uuid}"); + + if(signature is not null) + sb.AppendLine().Append($"Receiver Signature: {signature}"); + + if (message is not null) + sb.AppendLine().Append(message); + + logger.Log(LogLevel.Error, sb.ToString(), args); + } + + public static string ToTitle(this (string? UUID, string? Signature) envelopeReceiverTuple) + { + return $"Envelope UUID: {envelopeReceiverTuple.UUID}\n Receiver Signature: {envelopeReceiverTuple.Signature}"; + } + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/MessageKey.cs b/EnvelopeGenerator.Application/MessageKey.cs new file mode 100644 index 00000000..54469ef2 --- /dev/null +++ b/EnvelopeGenerator.Application/MessageKey.cs @@ -0,0 +1,19 @@ +namespace EnvelopeGenerator.Application +{ + public enum MessageKey + { + EnvelopeNotFound, + EnvelopeReceiverNotFound, + AccessCodeNull2Client, + AccessCodeNull2Logger, + WrongAccessCode, + DataIntegrityIssue, + SecurityBreachOrDataIntegrity, + PossibleDataIntegrityIssue, + SecurityBreach, + PossibleSecurityBreach, + WrongEnvelopeReceiverId2Client, //Do not leak information about the creation of the url. For example, the envelope you are looking for does not exist + WrongEnvelopeReceiverId2Logger, + EnvelopeOrReceiverNonexists + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Services/EnvelopeGeneratorExtensions.cs b/EnvelopeGenerator.Application/Services/EnvelopeGeneratorExtensions.cs index d111202b..7eb3e9cd 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeGeneratorExtensions.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeGeneratorExtensions.cs @@ -5,13 +5,76 @@ /// public static class EnvelopeGeneratorExtensions { + /// + /// Validates whether a given string is a correctly formatted Base-64 encoded string. + /// + /// + /// 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). + /// + /// The Base-64 encoded string to validate. + /// + /// true if the string is a valid Base-64 encoded string; otherwise, false. + /// + /// + /// + /// string testString = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnk="; + /// bool isValid = IsValidBase64String(testString); + /// Console.WriteLine(isValid); // Output: true + /// + /// + 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 + } + /// /// Decodes the envelope receiver ID and extracts the envelope UUID and receiver signature. /// /// The base64 encoded string containing the envelope UUID and receiver signature. /// A tuple containing the envelope UUID and receiver signature. - 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); diff --git a/EnvelopeGenerator.Application/Services/EnvelopeHistoryService.cs b/EnvelopeGenerator.Application/Services/EnvelopeHistoryService.cs index 67784a0f..f2f9fd63 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeHistoryService.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeHistoryService.cs @@ -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 CountAsync(int? envelopeId = null, string? userReference = null, int? status = null) => await _repository.CountAsync(envelopeId: envelopeId, userReference: userReference, status: status); + + public async Task AccessCodeAlreadyRequested(int envelopeId, string userReference) => await _repository.CountAsync(envelopeId: envelopeId, userReference:userReference, status: (int) EnvelopeStatus.AccessCodeRequested) > 0; } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs b/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs index 787ed26f..bea643f2 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs @@ -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 VerifyAccessCode(string envelopeUuid, string accessCode) + public async Task>> 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>(env_rcvs)); + } + + public async Task>> 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>(env_rcvs)); + } + + public async Task> 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() + .WithClientMessageKey(MessageKey.EnvelopeReceiverNotFound); + + return Successful(_mapper.MapOrThrow(env_rcv)); + } + + public async Task> ReadByEnvelopeReceiverIdAsync(string envelopeReceiverId, bool withEnvelope = true, bool withReceiver = true) + { + (string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId(); + + if (uuid is null || signature is null) + return Failed() + .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> VerifyAccessCodeAsync(string uuid, string signature, string accessCode) + { + var er = await _repository.ReadByUuidSignatureAsync(uuid: uuid, signature: signature); + + if (er is null) + return Failed() + .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() + .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> VerifyAccessCodeAsync(string envelopeReceiverId, string accessCode) + { + (string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId(); + + if (uuid is null || signature is null) + return Failed() + .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); } } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Services/EnvelopeService.cs b/EnvelopeGenerator.Application/Services/EnvelopeService.cs index efd6055f..9bdd7837 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeService.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeService.cs @@ -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, IEnvelopeService { - public EnvelopeService(IEnvelopeRepository repository, IKeyTranslationService translationService, IMapper mapper) + private readonly ILogger _logger; + public EnvelopeService(IEnvelopeRepository repository, IKeyTranslationService translationService, IMapper mapper, ILogger logger) : base(repository, translationService, mapper) { + _logger = logger; } - public async Task>> ReadAllWithAsync(bool documents = false, bool receivers = false, bool history = false, bool documentReceiverElement = false) + public async Task>> 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>(envelopes); return Successful(readDto); } - public async Task> ReadByUuidAsync(string uuid, string? signature = null, bool withDocuments = false, bool withReceivers = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withAll = false) + public async Task> 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(); diff --git a/EnvelopeGenerator.Domain/Constants.cs b/EnvelopeGenerator.Domain/Constants.cs deleted file mode 100644 index eeccbea2..00000000 --- a/EnvelopeGenerator.Domain/Constants.cs +++ /dev/null @@ -1,119 +0,0 @@ -namespace EnvelopeGenerator.Domain -{ - public class Constants - { - #region Status Fields - public enum EnvelopeStatus - { - Invalid = 0, - EnvelopeCreated = 1001, - EnvelopeSaved = 1002, - EnvelopeQueued = 1003, - EnvelopeSent = 1004, // Not used - EnvelopePartlySigned = 1005, - EnvelopeCompletelySigned = 1006, - EnvelopeReportCreated = 1007, - EnvelopeArchived = 1008, - EnvelopeDeleted = 1009, - AccessCodeRequested = 2001, - AccessCodeCorrect = 2002, - AccessCodeIncorrect = 2003, - DocumentOpened = 2004, - DocumentSigned = 2005, - SignatureConfirmed = 2006, - MessageInvitationSent = 3001, // Used by Trigger - MessageAccessCodeSent = 3002, - MessageConfirmationSent = 3003, - MessageDeletionSent = 3004, - MessageCompletionSent = 3005 - } - - public enum ElementStatus - { - Created = 0 - } - - public enum DocumentStatus - { - Created = 0, - Signed = 1 - } - - public enum ReceiverStatus - { - Unsigned = 0, - Signed = 1 - } - - #endregion - - #region Type Fields - - public enum ElementType - { - Signature = 1 - } - - public enum ContractType - { - Contract = 1, - ReadAndSign = 2 - } - - public enum ColorType - { - ReceiverColor1 = 1, - ReceiverColor2 = 2, - ReceiverColor3 = 3, - ReceiverColor4 = 4, - ReceiverColor5 = 5, - ReceiverColor6 = 6, - ReceiverColor7 = 7, - ReceiverColor8 = 8, - ReceiverColor9 = 9, - ReceiverColor10 = 10 - } - - public enum CertificationType - { - ElectronicSignature = 1, - QualifiedSignature = 2 - } - - public enum FinalEmailType - { - No = 0, - Yes = 1, - YesWithAttachment = 2 - } - - public enum PageOrientation - { - Portrait = 0, - Landscape = 1 - } - - public enum EmailTemplateType - { - DocumentReceived, - DocumentSigned, - DocumentDeleted, - DocumentCompleted, - DocumentAccessCodeReceived - } - - #endregion - - #region Constants - - public const string DATABASE = "DATABASE"; - public const string LOGCONFIG = "LOGCONFIG"; - public const string GDPICTURE = "GDPICTURE"; - - public const string GREEN_300 = "#bbf7d0"; - public const string RED_300 = "#fecaca"; - public const string ORANGE_300 = "#fed7aa"; - - #endregion - } -} \ No newline at end of file diff --git a/EnvelopeGenerator.Domain/Entities/Envelope.cs b/EnvelopeGenerator.Domain/Entities/Envelope.cs index 95e17c12..710ceb5c 100644 --- a/EnvelopeGenerator.Domain/Entities/Envelope.cs +++ b/EnvelopeGenerator.Domain/Entities/Envelope.cs @@ -1,4 +1,5 @@ -using EnvelopeGenerator.Common.My.Resources; +using EnvelopeGenerator.Common; +using EnvelopeGenerator.Common.My.Resources; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -84,8 +85,11 @@ namespace EnvelopeGenerator.Domain.Entities [Column("DMZ_MOVED")] public bool DmzMoved { get; set; } + /// + /// The sender of envelope + /// [ForeignKey("UserId")] - public Receiver? User { get; set; } + public DigitalData.UserManager.Domain.Entities.User? User { get; set; } [ForeignKey("EnvelopeTypeId")] public EnvelopeType? EnvelopeType { get; set; } @@ -111,7 +115,7 @@ namespace EnvelopeGenerator.Domain.Entities public IEnumerable? Documents { get; set; } - public IEnumerable? Receivers { get; set; } + public IEnumerable? EnvelopeReceivers { get; set; } public IEnumerable? History { get; set; } } diff --git a/EnvelopeGenerator.Domain/Entities/EnvelopeReceiver.cs b/EnvelopeGenerator.Domain/Entities/EnvelopeReceiver.cs index 6c78bfa5..beef339d 100644 --- a/EnvelopeGenerator.Domain/Entities/EnvelopeReceiver.cs +++ b/EnvelopeGenerator.Domain/Entities/EnvelopeReceiver.cs @@ -19,19 +19,19 @@ namespace EnvelopeGenerator.Domain.Entities public int Sequence { get; set; } [Column("NAME", TypeName = "nvarchar(128)")] - public string Name { get; set; } + public string? Name { get; set; } [Column("JOB_TITLE", TypeName = "nvarchar(128)")] - public string JobTitle { get; set; } + public string? JobTitle { get; set; } [Column("COMPANY_NAME", TypeName = "nvarchar(128)")] - public string CompanyName { get; set; } + public string? CompanyName { get; set; } [Column("PRIVATE_MESSAGE", TypeName = "nvarchar(max)")] - public string PrivateMessage { get; set; } + public string? PrivateMessage { get; set; } [Column("ACCESS_CODE", TypeName = "nvarchar(64)")] - public string AccessCode { get; set; } + public string? AccessCode { get; set; } [Required] [Column("ADDED_WHEN", TypeName = "datetime")] diff --git a/EnvelopeGenerator.Domain/Entities/Receiver.cs b/EnvelopeGenerator.Domain/Entities/Receiver.cs index a447e770..193ba895 100644 --- a/EnvelopeGenerator.Domain/Entities/Receiver.cs +++ b/EnvelopeGenerator.Domain/Entities/Receiver.cs @@ -22,7 +22,5 @@ namespace EnvelopeGenerator.Domain.Entities [Required] [Column("ADDED_WHEN", TypeName = "datetime")] public DateTime AddedWhen { get; set; } - - public IEnumerable? EnvelopeReceivers { get; set; } } } \ No newline at end of file diff --git a/EnvelopeGenerator.Domain/EnvelopeGenerator.Domain.csproj b/EnvelopeGenerator.Domain/EnvelopeGenerator.Domain.csproj index 49f45937..5f342ff9 100644 --- a/EnvelopeGenerator.Domain/EnvelopeGenerator.Domain.csproj +++ b/EnvelopeGenerator.Domain/EnvelopeGenerator.Domain.csproj @@ -10,4 +10,10 @@ + + + ..\..\WebUserManager\DigitalData.UserManager.Domain\bin\Debug\net7.0\DigitalData.UserManager.Domain.dll + + + diff --git a/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeHistoryRepository.cs b/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeHistoryRepository.cs index 264e31b9..e442dac2 100644 --- a/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeHistoryRepository.cs +++ b/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeHistoryRepository.cs @@ -5,5 +5,6 @@ namespace EnvelopeGenerator.Infrastructure.Contracts { public interface IEnvelopeHistoryRepository : ICRUDRepository { + Task CountAsync(int? envelopeId = null, string? userReference = null, int? status = null); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeReceiverRepository.cs b/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeReceiverRepository.cs index 816baf10..ba7e7de2 100644 --- a/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeReceiverRepository.cs +++ b/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeReceiverRepository.cs @@ -5,6 +5,12 @@ namespace EnvelopeGenerator.Infrastructure.Contracts { public interface IEnvelopeReceiverRepository : ICRUDRepository { - Task ReadAccessCodeByEnvelopeUuid(string envelopeUuid); + Task> ReadByUuidAsync(string uuid, bool withEnvelope = true, bool withReceiver = false); + + Task> ReadBySignatureAsync(string signature, bool withEnvelope = false, bool withReceiver = true); + + Task ReadByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true); + + Task ReadAccessCodeAsync(string uuid, string signature); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeRepository.cs b/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeRepository.cs index 132c7d06..facf3815 100644 --- a/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeRepository.cs +++ b/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeRepository.cs @@ -5,8 +5,8 @@ namespace EnvelopeGenerator.Infrastructure.Contracts { public interface IEnvelopeRepository : ICRUDRepository { - Task> ReadAllWithAsync(bool documents = false, bool receivers = false, bool history = false, bool documentReceiverElement = true); + Task> ReadAllWithAsync(bool documents = false, bool envelopeReceivers = false, bool history = false, bool documentReceiverElement = true); - Task ReadByUuidAsync(string uuid, string? signature = null, bool withDocuments = false, bool withReceivers = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withAll = false); + Task ReadByUuidAsync(string uuid, string? signature = null, bool withDocuments = false, bool withEnvelopeReceivers = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withUser = false, bool withAll = false); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj b/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj index 208721a6..fdeb1594 100644 --- a/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj +++ b/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj @@ -26,6 +26,9 @@ ..\..\WebCoreModules\DigitalData.Core.Infrastructure\bin\Debug\net7.0\DigitalData.Core.Infrastructure.dll + + ..\..\WebUserManager\DigitalData.UserManager.Domain\bin\Debug\net7.0\DigitalData.UserManager.Domain.dll + diff --git a/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeHistoryRepository.cs b/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeHistoryRepository.cs index 7c19c82a..e27286fa 100644 --- a/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeHistoryRepository.cs +++ b/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeHistoryRepository.cs @@ -2,6 +2,7 @@ using DigitalData.UserManager.Infrastructure.Repositories; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; +using Microsoft.EntityFrameworkCore; namespace EnvelopeGenerator.Infrastructure.Repositories { @@ -10,5 +11,21 @@ namespace EnvelopeGenerator.Infrastructure.Repositories public EnvelopeHistoryRepository(EGDbContext dbContext) : base(dbContext) { } + + public async Task CountAsync(int? envelopeId = null, string? userReference = null, int? status = null) + { + var query = _dbSet.AsQueryable(); + + if (envelopeId is not null) + query = query.Where(eh => eh.EnvelopeId == envelopeId); + + if (userReference is not null) + query = query.Where(eh => eh.UserReference == userReference); + + if (status is not null) + query = query.Where(eh => eh.Status == status); + + return await query.CountAsync(); + } } } \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeRepository.cs b/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeRepository.cs index 0c6244eb..0059ac3e 100644 --- a/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeRepository.cs +++ b/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeRepository.cs @@ -23,7 +23,7 @@ namespace EnvelopeGenerator.Infrastructure.Repositories query = query.Include(e => e.Documents); if (receivers) - query = query.Include(e => e.Receivers); + query = query.Include(e => e.EnvelopeReceivers); if (history) query = query.Include(e => e.History); @@ -31,12 +31,12 @@ namespace EnvelopeGenerator.Infrastructure.Repositories return await query.ToListAsync(); } - public async Task ReadByUuidAsync(string uuid, string? signature = null, bool withDocuments = false, bool withReceivers = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withAll = false) + public async Task 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 query = _dbSet.Where(e => e.Uuid == uuid); if (signature is not null) - query = query.Where(e => e.Receivers != null && e.Receivers.Any(er => er.Receiver != null && er.Receiver.Signature == signature)); + query = query.Where(e => e.EnvelopeReceivers != null && e.EnvelopeReceivers.Any(er => er.Receiver != null && er.Receiver.Signature == signature)); if (withAll || withDocuments) if (withAll || withDocumentReceiverElement) @@ -44,8 +44,11 @@ namespace EnvelopeGenerator.Infrastructure.Repositories else query = query.Include(e => e.Documents); - if (withAll || withReceivers) - query = query.Include(e => e.Receivers!).ThenInclude(er => er.Receiver); + if (withAll || withEnvelopeReceivers) + query = query.Include(e => e.EnvelopeReceivers!).ThenInclude(er => er.Receiver); + + if (withAll || withUser) + query = query.Include(e => e.User!); if (withAll || withHistory) query = query.Include(e => e.History); diff --git a/EnvelopeGenerator.Infrastructure/Repositories/EnvlopeReceiverRepository.cs b/EnvelopeGenerator.Infrastructure/Repositories/EnvlopeReceiverRepository.cs index 0f528f41..6fb35346 100644 --- a/EnvelopeGenerator.Infrastructure/Repositories/EnvlopeReceiverRepository.cs +++ b/EnvelopeGenerator.Infrastructure/Repositories/EnvlopeReceiverRepository.cs @@ -3,6 +3,7 @@ using DigitalData.UserManager.Infrastructure.Repositories; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; using Microsoft.EntityFrameworkCore; +using System; namespace EnvelopeGenerator.Infrastructure.Repositories { @@ -12,14 +13,37 @@ namespace EnvelopeGenerator.Infrastructure.Repositories { } - public async Task ReadAccessCodeByEnvelopeUuid(string envelopeUuid) + private IQueryable ReadWhere(string? uuid = null, string? signature = null, bool withEnvelope = false, bool withReceiver = false) { - var accessCode = await _dbSet - .Where(er => er.Envelope != null && er.Envelope.Uuid == envelopeUuid) + var query = _dbSet.AsQueryable(); + + if(uuid is not null) + query = query.Where(er => er.Envelope != null && er.Envelope.Uuid == uuid); + + if (signature is not null) + query = query.Where(er => er.Receiver != null && er.Receiver.Signature == signature); + + if (withEnvelope) + query = query.Include(er => er.Envelope); + + if (withReceiver) + query = query.Include(er => er.Receiver); + + return query; + } + + public async Task> ReadByUuidAsync(string uuid, bool withEnvelope = true, bool withReceiver = false) + => await ReadWhere(uuid: uuid, withEnvelope: withEnvelope, withReceiver: withReceiver).ToListAsync(); + + public async Task> ReadBySignatureAsync(string signature, bool withEnvelope = false, bool withReceiver = true) + => await ReadWhere(signature: signature, withEnvelope: withEnvelope, withReceiver: withReceiver).ToListAsync(); + + public async Task ReadByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true) + => await ReadWhere(uuid: uuid, signature: signature, withEnvelope: withEnvelope, withReceiver: withReceiver).FirstOrDefaultAsync(); + + public async Task ReadAccessCodeAsync(string uuid, string signature) + => await ReadWhere(uuid:uuid, signature:signature) .Select(er => er.AccessCode) .FirstOrDefaultAsync(); - - return accessCode; - } } } \ No newline at end of file diff --git a/EnvelopeGenerator.Service/App.config b/EnvelopeGenerator.Service/App.config index a99d30ce..8949adf6 100644 --- a/EnvelopeGenerator.Service/App.config +++ b/EnvelopeGenerator.Service/App.config @@ -1,12 +1,12 @@ - -
+ +
- + diff --git a/EnvelopeGenerator.Service/EnvelopeGenerator.Service.vbproj b/EnvelopeGenerator.Service/EnvelopeGenerator.Service.vbproj index 54b8f039..85a7395e 100644 --- a/EnvelopeGenerator.Service/EnvelopeGenerator.Service.vbproj +++ b/EnvelopeGenerator.Service/EnvelopeGenerator.Service.vbproj @@ -11,7 +11,7 @@ EnvelopeGenerator.Service 512 Console - v4.6.2 + v4.8 true diff --git a/EnvelopeGenerator.Service/My Project/Application.Designer.vb b/EnvelopeGenerator.Service/My Project/Application.Designer.vb index 8ab460ba..88dd01c7 100644 --- a/EnvelopeGenerator.Service/My Project/Application.Designer.vb +++ b/EnvelopeGenerator.Service/My Project/Application.Designer.vb @@ -1,10 +1,10 @@ '------------------------------------------------------------------------------ ' -' Dieser Code wurde von einem Tool generiert. -' Laufzeitversion:4.0.30319.42000 +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 ' -' Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn -' der Code erneut generiert wird. +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. ' '------------------------------------------------------------------------------ diff --git a/EnvelopeGenerator.Service/My Project/Resources.Designer.vb b/EnvelopeGenerator.Service/My Project/Resources.Designer.vb index b08dad5c..6bdb5758 100644 --- a/EnvelopeGenerator.Service/My Project/Resources.Designer.vb +++ b/EnvelopeGenerator.Service/My Project/Resources.Designer.vb @@ -1,10 +1,10 @@ '------------------------------------------------------------------------------ ' -' Dieser Code wurde von einem Tool generiert. -' Laufzeitversion:4.0.30319.42000 +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 ' -' Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn -' der Code erneut generiert wird. +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. ' '------------------------------------------------------------------------------ @@ -15,12 +15,12 @@ Imports System Namespace My.Resources - 'Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert - '-Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. - 'Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen - 'mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + 'This class was auto-generated by the StronglyTypedResourceBuilder + 'class via a tool like ResGen or Visual Studio. + 'To add or remove a member, edit your .ResX file then rerun ResGen + 'with the /str option, or rebuild your VS project. ''' - ''' Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + ''' A strongly-typed resource class, for looking up localized strings, etc. ''' - ''' Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + ''' Returns the cached ResourceManager instance used by this class. ''' _ Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager @@ -47,8 +47,8 @@ Namespace My.Resources End Property ''' - ''' Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle - ''' Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + ''' Overrides the current thread's CurrentUICulture property for all + ''' resource lookups using this strongly typed resource class. ''' _ Friend Property Culture() As Global.System.Globalization.CultureInfo diff --git a/EnvelopeGenerator.Service/My Project/Settings.Designer.vb b/EnvelopeGenerator.Service/My Project/Settings.Designer.vb index 223ade22..2b9420b0 100644 --- a/EnvelopeGenerator.Service/My Project/Settings.Designer.vb +++ b/EnvelopeGenerator.Service/My Project/Settings.Designer.vb @@ -1,10 +1,10 @@ '------------------------------------------------------------------------------ ' -' Dieser Code wurde von einem Tool generiert. -' Laufzeitversion:4.0.30319.42000 +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 ' -' Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn -' der Code erneut generiert wird. +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. ' '------------------------------------------------------------------------------ @@ -15,14 +15,14 @@ Option Explicit On Namespace My _ Partial Friend NotInheritable Class MySettings Inherits Global.System.Configuration.ApplicationSettingsBase Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings) -#Region "Automatische My.Settings-Speicherfunktion" +#Region "My.Settings Auto-Save Functionality" #If _MyType = "WindowsForms" Then Private Shared addedHandler As Boolean diff --git a/EnvelopeGenerator.Web/Controllers/BaseController.cs b/EnvelopeGenerator.Web/Controllers/BaseController.cs index 28f90b18..0906c6e2 100644 --- a/EnvelopeGenerator.Web/Controllers/BaseController.cs +++ b/EnvelopeGenerator.Web/Controllers/BaseController.cs @@ -1,8 +1,6 @@ -using DigitalData.Modules.Logging; -using EnvelopeGenerator.Common; +using EnvelopeGenerator.Common; using EnvelopeGenerator.Web.Services; using Microsoft.AspNetCore.Mvc; -using System.Text; namespace EnvelopeGenerator.Web.Controllers { diff --git a/EnvelopeGenerator.Web/Controllers/ControllerBaseExtensions.cs b/EnvelopeGenerator.Web/Controllers/ControllerBaseExtensions.cs index 6616b3fa..be73e434 100644 --- a/EnvelopeGenerator.Web/Controllers/ControllerBaseExtensions.cs +++ b/EnvelopeGenerator.Web/Controllers/ControllerBaseExtensions.cs @@ -39,4 +39,4 @@ namespace EnvelopeGenerator.Web.Controllers return null; } } -} +} \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Controllers/HomeController.cs b/EnvelopeGenerator.Web/Controllers/HomeController.cs index 5c4426bc..cb82faa9 100644 --- a/EnvelopeGenerator.Web/Controllers/HomeController.cs +++ b/EnvelopeGenerator.Web/Controllers/HomeController.cs @@ -9,6 +9,8 @@ using Microsoft.AspNetCore.Mvc; using System.Diagnostics; using System.Security.Claims; using Microsoft.AspNetCore.Authorization; +using DigitalData.Core.API; +using DigitalData.Core.Application; namespace EnvelopeGenerator.Web.Controllers { @@ -18,136 +20,144 @@ namespace EnvelopeGenerator.Web.Controllers private readonly IConfiguration _config; private readonly IEnvelopeReceiverService _envRcvService; private readonly IEnvelopeService _envelopeService; - - public HomeController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger logger, IConfiguration configuration, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeService envelopeService) : base(databaseService, logger) + private readonly IEnvelopeHistoryService _historyService; + public HomeController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger logger, IConfiguration configuration, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeService envelopeService, IEnvelopeHistoryService historyService) : base(databaseService, logger) { this.envelopeOldService = envelopeOldService; _envRcvService = envelopeReceiverService; _envelopeService = envelopeService; _config = configuration; + _historyService = historyService; } - - [HttpGet("/")] - public IActionResult Index() - { - return View(); - } - - [HttpPost("/")] - public IActionResult DebugEnvelopes([FromForm] string? password) - { - try - { - var passwordFromConfig = _config["Config:AdminPassword"]; - - if (passwordFromConfig == null) - { - ViewData["error"] = "No admin password configured!"; - return View("Index"); - } - - if (password != passwordFromConfig) - { - ViewData["error"] = "Wrong Password!"; - return View("Index"); - } - - List envelopes = envelopeOldService.LoadEnvelopes(); - - return View(envelopes); - } - catch(Exception ex) - { - _logger.LogError(ex, "Unexpected error"); - ViewData["error"] = "Unknown error!"; - return View("Index"); - } - } - + [HttpGet("/EnvelopeKey/{envelopeReceiverId}")] public async Task SendAccessCode([FromRoute] string envelopeReceiverId) { - var envelope = await _envelopeService.ReadByUuidAsync(envelopeReceiverId.GetEnvelopeUuid()); - - EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId); - - if (response.Envelope.UseAccessCode) + ViewData["EnvelopeKey"] = envelopeReceiverId; + try { - bool accessCodeAlreadyRequested = database.Models.receiverModel.AccessCodeAlreadyRequested(response.Receiver.Email, response.Envelope.Id); + (string envelopeUuid, string receiverSignature) = envelopeReceiverId.DecodeEnvelopeReceiverId(); + var envelopeResult = await _envelopeService.ReadByUuidAsync(envelopeUuid, signature: receiverSignature, withEnvelopeReceivers:true); + var envelope = envelopeResult.Data; + var envelopeReceiver = envelope?.EnvelopeReceivers?.FirstOrDefault(); + var mailAddress = envelopeReceiver?.Receiver?.EmailAddress; + var useAccessCode = envelope?.UseAccessCode; - if (!accessCodeAlreadyRequested) + ViewData["EnvelopeResult"] = envelopeResult; + if(envelopeResult.IsSuccess && envelope is not null && mailAddress is not null && (envelope.UseAccessCode ?? false)) + { + EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId); + + bool accessCodeAlreadyRequested = database.Models.receiverModel.AccessCodeAlreadyRequested(response.Receiver.Email, response.Envelope.Id); + accessCodeAlreadyRequested = await _historyService.AccessCodeAlreadyRequested(envelopeId: envelope.Id, userReference: mailAddress); + if (!accessCodeAlreadyRequested) + { + // Send email with password + bool actionResult = database.Services.actionService.RequestAccessCode(response.Envelope, response.Receiver); + bool result = database.Services.emailService.SendDocumentAccessCodeReceivedEmail(response.Envelope, response.Receiver); + } + } + else { - // Send email with password - bool actionResult = database.Services.actionService.RequestAccessCode(response.Envelope, response.Receiver); - bool result = database.Services.emailService.SendDocumentAccessCodeReceivedEmail(response.Envelope, response.Receiver); + envelopeResult.WithMessageKey(MessageKey.FailedToSendAccessCode); + _logger.LogError($"{MessageKey.FailedToSendAccessCode.ToString()}"); } } + catch(Exception ex) + { + _logger.LogError(ex, MessageKey.UnexpectedError.ToString()); + } - return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Locked"); + return View("EnvelopeLocked").WithData("EnvelopeKey", envelopeReceiverId); } - [HttpGet("/EnvelopeKey/{envelopeReceiverId}/Locked")] + [HttpGet("EnvelopeKey/{envelopeReceiverId}/Locked")] public IActionResult EnvelopeLocked([FromRoute] string envelopeReceiverId) { - ViewData["EnvelopeKey"] = envelopeReceiverId; - return View(); + return View().WithData("EnvelopeKey", envelopeReceiverId); } [HttpPost("/EnvelopeKey/{envelopeReceiverId}/Locked")] public async Task LogInEnvelope([FromRoute] string envelopeReceiverId, [FromForm] string access_code) { - var decodedId = envelopeReceiverId.DecodeEnvelopeReceiverId(); - - _logger.LogInformation($"Envelope UUID: [{decodedId.EnvelopeUuid}]"); - _logger.LogInformation($"Receiver Signature: [{decodedId.ReceiverSignature}]"); - - var verification = await _envRcvService.VerifyAccessCode(decodedId.EnvelopeUuid, access_code); - EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId); - - if (verification.IsSuccess) + try { - if (envelopeOldService.ReceiverAlreadySigned(response.Envelope, response.Receiver.Id)) + (string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId(); + + if(uuid is null || signature is null) { - return Redirect("/EnvelopeKey/{envelopeReceiverId}/Success"); + _logger.LogWarning($"{MessageKey.WrongEnvelopeReceiverId.ToString()}"); + return BadRequest(_envelopeService.CreateMessage(false, MessageKey.WrongEnvelopeReceiverId.ToString())); } - var envelope = await _envelopeService.ReadByUuidAsync(uuid: decodedId.EnvelopeUuid, signature: decodedId.ReceiverSignature, withAll: true); - database.Services.actionService.EnterCorrectAccessCode(response.Envelope, response.Receiver); //for history - ViewData["EnvelopeKey"] = envelopeReceiverId; - ViewData["EnvelopeResponse"] = response; + _logger.LogInformation($"Envelope UUID: [{uuid}]\nReceiver Signature: [{signature}]"); - if (response.Envelope.Documents.Count() > 0) + var verification = await _envRcvService.VerifyAccessCodeAsync(uuid: uuid, signature: signature, accessCode: access_code); + var isVerified = verification.Data; + + EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId); + + if (!verification.IsSuccess) { - var document = await envelopeOldService.GetDocument(response.Envelope.Documents[0].Id, envelopeReceiverId); - byte[] bytes = await envelopeOldService.GetDocumentContents(document); - ViewData["DocumentBytes"] = bytes; + _logger.LogServiceMessage(verification); + + if (verification.HasFlag(Flag.SecurityBreach)) + return Forbid(); + + return StatusCode(StatusCodes.Status500InternalServerError, verification.ClientMessages.Join()); + } + else if (isVerified) + { + if (envelopeOldService.ReceiverAlreadySigned(response.Envelope, response.Receiver.Id)) + { + return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Success"); + } + + var envelope = await _envelopeService.ReadByUuidAsync(uuid: uuid, signature: signature, withAll: true); + + database.Services.actionService.EnterCorrectAccessCode(response.Envelope, response.Receiver); //for history + ViewData["EnvelopeKey"] = envelopeReceiverId; + ViewData["EnvelopeResponse"] = response; + + if (response.Envelope.Documents.Count() > 0) + { + var document = await envelopeOldService.GetDocument(response.Envelope.Documents[0].Id, envelopeReceiverId); + byte[] bytes = await envelopeOldService.GetDocumentContents(document); + ViewData["DocumentBytes"] = bytes; + } + else + ViewData["DocumentBytes"] = null; + + var claims = new List + { + new Claim(ClaimTypes.NameIdentifier, uuid), + new Claim(ClaimTypes.Hash, signature), + }; + + var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); + var authProperties = new AuthenticationProperties + { + }; + + await HttpContext.SignInAsync( + CookieAuthenticationDefaults.AuthenticationScheme, + new ClaimsPrincipal(claimsIdentity), + authProperties); + + return View("ShowEnvelope", envelope); } else - ViewData["DocumentBytes"] = null; - - var claims = new List { - new Claim(ClaimTypes.NameIdentifier, decodedId.EnvelopeUuid), - new Claim(ClaimTypes.Hash, decodedId.ReceiverSignature), - }; + database.Services.actionService.EnterIncorrectAccessCode(response.Envelope, response.Receiver); //for history + _logger.LogWarning(string.Join("\n", verification.Messages)); + return Unauthorized(); - var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); - var authProperties = new AuthenticationProperties - { - }; - - await HttpContext.SignInAsync( - CookieAuthenticationDefaults.AuthenticationScheme, - new ClaimsPrincipal(claimsIdentity), - authProperties); - - return View("ShowEnvelope", envelope); - } - else + } + } + catch(Exception ex) { - database.Services.actionService.EnterIncorrectAccessCode(response.Envelope, response.Receiver); //for history - return Unauthorized(); - + _logger.LogError(ex, MessageKey.UnexpectedError.ToString()); + return this.InnerServiceError(messageKey: MessageKey.UnexpectedError); } } @@ -168,14 +178,11 @@ namespace EnvelopeGenerator.Web.Controllers return Ok(new { EnvelopeUuid = envelopeUuid, ReceiverSignature = receiverSignature }); } + [HttpGet("Error")] [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } - - [Authorize] - [HttpGet("test")] - public string Test() => "Test"; } } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeController.cs b/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeController.cs index e7d84eea..16785f38 100644 --- a/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeController.cs +++ b/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeController.cs @@ -14,13 +14,10 @@ namespace EnvelopeGenerator.Web.Controllers.Test } [NonAction] - public override Task GetAll() - { - return base.GetAll(); - } + public override Task GetAll() => base.GetAll(); [HttpGet] - public virtual async Task GetAll([FromQuery] string? envelopeKey = default, [FromQuery] bool withDocuments = false, [FromQuery] bool withReceivers = false, [FromQuery] bool withHistory = false, [FromQuery] bool withDocumentReceiverElement = false, [FromQuery] bool withAll = true) + public async Task GetAll([FromQuery] string? envelopeKey = default, [FromQuery] bool withDocuments = false, [FromQuery] bool withEnvelopeReceivers = false, [FromQuery] bool withHistory = false, [FromQuery] bool withDocumentReceiverElement = false, [FromQuery] bool withUser = false, [FromQuery] bool withAll = true) { if(envelopeKey is not null) { @@ -29,7 +26,7 @@ namespace EnvelopeGenerator.Web.Controllers.Test var envlopeServiceResult = await _service.ReadByUuidAsync( uuid: decoded.EnvelopeUuid, signature: decoded.ReceiverSignature, - withDocuments: withDocuments, withReceivers: withReceivers, withHistory: withHistory, withDocumentReceiverElement:withDocumentReceiverElement, withAll:withAll); + withDocuments: withDocuments, withEnvelopeReceivers: withEnvelopeReceivers, withHistory: withHistory, withDocumentReceiverElement:withDocumentReceiverElement, withUser:withUser, withAll:withAll); if (envlopeServiceResult.IsSuccess) { @@ -38,7 +35,7 @@ namespace EnvelopeGenerator.Web.Controllers.Test return NotFound(); } - var result = await _service.ReadAllWithAsync(documents: withDocuments, receivers: withReceivers, history: withHistory); + var result = await _service.ReadAllWithAsync(documents: withDocuments, envelopeReceivers: withEnvelopeReceivers, history: withHistory); if (result.IsSuccess) { return Ok(result); diff --git a/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeHistoryController.cs b/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeHistoryController.cs index 98c990f8..0164c04e 100644 --- a/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeHistoryController.cs +++ b/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeHistoryController.cs @@ -3,6 +3,7 @@ using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; +using Microsoft.AspNetCore.Mvc; namespace EnvelopeGenerator.Web.Controllers.Test { @@ -10,7 +11,18 @@ namespace EnvelopeGenerator.Web.Controllers.Test { public TestEnvelopeHistoryController(ILogger logger, IEnvelopeHistoryService service) : base(logger, service) { + } + [HttpGet("Count")] + public async Task Count(int? envelopeId = null, string? userReference = null, int? status = null) + { + return Ok(await _service.CountAsync(envelopeId, userReference, status)); + } + + [HttpGet("is-ac-req")] + public async Task AccessCodeAlreadyRequested(int envelopeId, string userReference) + { + return Ok(await _service.AccessCodeAlreadyRequested(envelopeId, userReference)); } } } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeReceiverController.cs b/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeReceiverController.cs index 073720ef..6d70ddca 100644 --- a/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeReceiverController.cs +++ b/EnvelopeGenerator.Web/Controllers/Test/TestEnvelopeReceiverController.cs @@ -1,8 +1,11 @@ using DigitalData.Core.API; +using DigitalData.Core.Application; using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.DTOs; +using EnvelopeGenerator.Application.Services; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; +using Microsoft.AspNetCore.Mvc; namespace EnvelopeGenerator.Web.Controllers.Test { @@ -10,7 +13,38 @@ namespace EnvelopeGenerator.Web.Controllers.Test { public TestEnvelopeReceiverController(ILogger logger, IEnvelopeReceiverService service) : base(logger, service) { + } + [HttpGet("verify-access-code/{envelope_receiver_id}")] + public async Task VerifyAccessCode([FromRoute] string envelope_receiver_id, [FromQuery] string access_code) + { + var verification = await _service.VerifyAccessCodeAsync(envelopeReceiverId:envelope_receiver_id, accessCode: access_code); + + if (verification.IsSuccess) + return Ok(verification); + else if (verification.HasFlag(Flag.SecurityBreach)) + return Forbid(); + else if (verification.HasFlag(Flag.SecurityBreachOrDataIntegrity)) + return Conflict(); + else + return this.InnerServiceError(verification); + } + + [HttpGet("e-r-id/{envelope_receiver_id}")] + public async Task GetByEnvelopeReceiverId([FromRoute] string envelope_receiver_id) + { + var er_result = await _service.ReadByEnvelopeReceiverIdAsync(envelopeReceiverId: envelope_receiver_id); + if (er_result.IsSuccess) + return Ok(er_result); + else + return this.InnerServiceError(er_result); + } + + [HttpGet("decode")] + public IActionResult DecodeEnvelopeReceiverId(string envelopeReceiverId) + { + var decoded = envelopeReceiverId.DecodeEnvelopeReceiverId(); + return Ok(new { uuid = decoded.EnvelopeUuid, signature = decoded.ReceiverSignature }); } } } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Controllers/Test/TestViewController.cs b/EnvelopeGenerator.Web/Controllers/Test/TestViewController.cs new file mode 100644 index 00000000..46bc0b36 --- /dev/null +++ b/EnvelopeGenerator.Web/Controllers/Test/TestViewController.cs @@ -0,0 +1,60 @@ +using EnvelopeGenerator.Application.Contracts; +using EnvelopeGenerator.Common; +using EnvelopeGenerator.Web.Services; +using Microsoft.AspNetCore.Mvc; + +namespace EnvelopeGenerator.Web.Controllers.Test +{ + public class TestViewController : BaseController + { + private readonly EnvelopeOldService envelopeOldService; + private readonly IConfiguration _config; + private readonly IEnvelopeReceiverService _envRcvService; + private readonly IEnvelopeService _envelopeService; + + public TestViewController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger logger, IConfiguration configuration, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeService envelopeService) : base(databaseService, logger) + { + this.envelopeOldService = envelopeOldService; + _envRcvService = envelopeReceiverService; + _envelopeService = envelopeService; + _config = configuration; + } + + [HttpGet("/")] + public IActionResult Index() + { + return View("Index"); + } + + [HttpPost("/")] + public IActionResult DebugEnvelopes([FromForm] string? password) + { + try + { + var passwordFromConfig = _config["Config:AdminPassword"]; + + if (passwordFromConfig == null) + { + ViewData["error"] = "No admin password configured!"; + return View("Index"); + } + + if (password != passwordFromConfig) + { + ViewData["error"] = "Wrong Password!"; + return View("Index"); + } + + List envelopes = envelopeOldService.LoadEnvelopes(); + + return View("DebugEnvelopes", envelopes); + } + catch(Exception ex) + { + _logger.LogError(ex, "Unexpected error"); + ViewData["error"] = "Unknown error!"; + return View("Index"); + } + } + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj b/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj index 77d36604..7cd5767a 100644 --- a/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj +++ b/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj @@ -25,6 +25,9 @@ + + + @@ -66,6 +69,15 @@ ..\..\DDModules\Logging\bin\Debug\DigitalData.Modules.Logging.dll + + ..\..\WebUserManager\DigitalData.UserManager.Application\bin\Debug\net7.0\DigitalData.UserManager.Application.dll + + + ..\..\WebUserManager\DigitalData.UserManager.Application\bin\Debug\net7.0\DigitalData.UserManager.Domain.dll + + + ..\..\WebUserManager\DigitalData.UserManager.Application\bin\Debug\net7.0\DigitalData.UserManager.Infrastructure.dll + D:\ProgramFiles\GdPicture.NET 14\Redist\GdPicture.NET (.NET Framework 4.5)\GdPicture.NET.14.dll diff --git a/EnvelopeGenerator.Web/MessageKey.cs b/EnvelopeGenerator.Web/MessageKey.cs new file mode 100644 index 00000000..47ccc718 --- /dev/null +++ b/EnvelopeGenerator.Web/MessageKey.cs @@ -0,0 +1,10 @@ +namespace EnvelopeGenerator.Web +{ + public enum MessageKey + { + UnexpectedError, + FailedToSendAccessCode, + WrongEnvelopeReceiverId, //the value should be about URL (like URL is not existing) as a part of security. + DataIntegrityError + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Program.cs b/EnvelopeGenerator.Web/Program.cs index c254425b..dae95eb4 100644 --- a/EnvelopeGenerator.Web/Program.cs +++ b/EnvelopeGenerator.Web/Program.cs @@ -13,6 +13,7 @@ using NLog.Web; using DigitalData.Core.API; using Microsoft.AspNetCore.Authentication.Cookies; using DigitalData.Core.Application; +using DigitalData.UserManager.Application.MappingProfiles; var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); logger.Info("Logging initialized!"); @@ -29,9 +30,10 @@ try // Add higher order services builder.Services.AddScoped(); - // Add services to the container. + // Add controllers and razor views builder.Services.AddControllersWithViews(options => { + //remove option for Test*Controller options.Conventions.Add(new RemoveIfControllerConvention() .AndIf(c => c.ControllerName.StartsWith("Test")) .AndIf(c => !builder.Configuration.GetValue("AddTestControllers"))); @@ -86,6 +88,7 @@ try //Auto mapping profiles builder.Services.AddAutoMapper(typeof(BasicDtoMappingProfile).Assembly); + builder.Services.AddAutoMapper(typeof(UserMappingProfile).Assembly); builder.Services.Configure(options => { diff --git a/EnvelopeGenerator.Web/Views/Home/EnvelopeLocked.cshtml b/EnvelopeGenerator.Web/Views/Home/EnvelopeLocked.cshtml index 30861534..0e824a25 100644 --- a/EnvelopeGenerator.Web/Views/Home/EnvelopeLocked.cshtml +++ b/EnvelopeGenerator.Web/Views/Home/EnvelopeLocked.cshtml @@ -1,7 +1,18 @@ @{ ViewData["Title"] = "Dokument geschützt"; } - +@if(ViewData["Test1"] is string test1) +{ + +} +@if (ViewData["Test2"] is string test2) +{ + +}
diff --git a/EnvelopeGenerator.Web/Views/Home/ShowEnvelope.cshtml b/EnvelopeGenerator.Web/Views/Home/ShowEnvelope.cshtml index bc2d46ff..613fba3e 100644 --- a/EnvelopeGenerator.Web/Views/Home/ShowEnvelope.cshtml +++ b/EnvelopeGenerator.Web/Views/Home/ShowEnvelope.cshtml @@ -9,7 +9,8 @@ { var envelope = Model.Data; var document = envelope.Documents?.FirstOrDefault(); - var receiver = envelope.Receivers?.FirstOrDefault(); + var receiver = envelope.EnvelopeReceivers?.FirstOrDefault(); + var sender = envelope.User; var receiverName = receiver?.Name ?? string.Empty; var pages = document?.Elements?.Select(e => e.Page) ?? Array.Empty(); var stPageIndexes = string.Join(pages.Count() > 1 ? ", " : "", pages.Take(pages.Count() - 1)) diff --git a/EnvelopeGenerator.Web/Views/Home/DebugEnvelopes.cshtml b/EnvelopeGenerator.Web/Views/TestView/DebugEnvelopes.cshtml similarity index 100% rename from EnvelopeGenerator.Web/Views/Home/DebugEnvelopes.cshtml rename to EnvelopeGenerator.Web/Views/TestView/DebugEnvelopes.cshtml diff --git a/EnvelopeGenerator.Web/Views/Home/Index.cshtml b/EnvelopeGenerator.Web/Views/TestView/Index.cshtml similarity index 100% rename from EnvelopeGenerator.Web/Views/Home/Index.cshtml rename to EnvelopeGenerator.Web/Views/TestView/Index.cshtml diff --git a/EnvelopeGenerator.Web/appsettings.json b/EnvelopeGenerator.Web/appsettings.json index e761e269..fd26c28d 100644 --- a/EnvelopeGenerator.Web/appsettings.json +++ b/EnvelopeGenerator.Web/appsettings.json @@ -21,21 +21,33 @@ "type": "File", "fileName": "E:\\EnvelopeGenerator\\Logs\\${shortdate}-ECM.EnvelopeGenerator.Web-Error.log", "maxArchiveDays": 30 + }, + "criticalLogs": { + "type": "File", + "fileName": "E:\\EnvelopeGenerator\\Logs\\${shortdate}-ECM.EnvelopeGenerator.Web-Critical.log", + "maxArchiveDays": 30 } }, + // Trace, Debug, Info, Warn, Error and Fatal "rules": [ { "logger": "*", "minLevel": "Info", + "maxLevel": "Warn", "writeTo": "infoLogs" }, { "logger": "*", - "minLevel": "Error", + "level": "Error", "writeTo": "errorLogs" }, { - "logger": "Namespace.Controllers.*", + "logger": "*", + "level": "Fatal", + "writeTo": "criticalLogs" + }, + { + "logger": "EnvelopeGenerator.Web.Controllers.*", "minLevel": "Error", "writeTo": "errorLogs", "final": true diff --git a/EnvelopeGenerator.Web/wwwroot/js/app.js b/EnvelopeGenerator.Web/wwwroot/js/app.js index bd32fe6b..a0e021c4 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/app.js +++ b/EnvelopeGenerator.Web/wwwroot/js/app.js @@ -35,10 +35,7 @@ class App { // Load the document from the filestore console.debug('Loading document from filestore') - const documentResponse = await this.Network.getDocument( - this.envelopeKey, - this.currentDocument.id - ) + const documentResponse = this.documentBytes if (documentResponse.fatal || documentResponse.error) { console.error(documentResponse.error)