From 6338b81571adf73a6a1198552795beccc836ddad Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 24 Apr 2024 13:45:03 +0200 Subject: [PATCH 1/3] =?UTF-8?q?Refaktorisierung:=20Absicherung=20von=20DB-?= =?UTF-8?q?Operationen=20und=20Verbesserung=20der=20Gesch=C3=A4ftslogik?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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. --- .../Contracts/IEnvelopeHistoryService.cs | 4 + .../Contracts/IEnvelopeReceiverService.cs | 13 +- .../Contracts/IEnvelopeService.cs | 4 +- .../DTOs/EnvelopeDto.cs | 7 +- .../DTOs/EnvelopeReceiverDto.cs | 8 +- .../DTOs/ReceiverDto.cs | 4 +- EnvelopeGenerator.Application/EnvelopeFlag.cs | 7 + .../EnvelopeGenerator.Application.csproj | 12 ++ .../EnvelopeGeneratorExtensions.cs | 39 ++++ EnvelopeGenerator.Application/MessageKey.cs | 19 ++ .../Services/EnvelopeGeneratorExtensions.cs | 65 +++++- .../Services/EnvelopeHistoryService.cs | 5 + .../Services/EnvelopeReceiverService.cs | 81 ++++++- .../Services/EnvelopeService.cs | 13 +- EnvelopeGenerator.Domain/Constants.cs | 119 ----------- EnvelopeGenerator.Domain/Entities/Envelope.cs | 10 +- .../Entities/EnvelopeReceiver.cs | 10 +- EnvelopeGenerator.Domain/Entities/Receiver.cs | 2 - .../EnvelopeGenerator.Domain.csproj | 6 + .../Contracts/IEnvelopeHistoryRepository.cs | 1 + .../Contracts/IEnvelopeReceiverRepository.cs | 8 +- .../Contracts/IEnvelopeRepository.cs | 4 +- .../EnvelopeGenerator.Infrastructure.csproj | 3 + .../Repositories/EnvelopeHistoryRepository.cs | 17 ++ .../Repositories/EnvelopeRepository.cs | 13 +- .../Repositories/EnvlopeReceiverRepository.cs | 36 +++- EnvelopeGenerator.Service/App.config | 6 +- .../EnvelopeGenerator.Service.vbproj | 2 +- .../My Project/Application.Designer.vb | 8 +- .../My Project/Resources.Designer.vb | 24 +-- .../My Project/Settings.Designer.vb | 12 +- .../Controllers/BaseController.cs | 4 +- .../Controllers/ControllerBaseExtensions.cs | 2 +- .../Controllers/HomeController.cs | 201 +++++++++--------- .../Test/TestEnvelopeController.cs | 11 +- .../Test/TestEnvelopeHistoryController.cs | 12 ++ .../Test/TestEnvelopeReceiverController.cs | 34 +++ .../Controllers/Test/TestViewController.cs | 60 ++++++ .../EnvelopeGenerator.Web.csproj | 12 ++ EnvelopeGenerator.Web/MessageKey.cs | 10 + EnvelopeGenerator.Web/Program.cs | 5 +- .../Views/Home/EnvelopeLocked.cshtml | 13 +- .../Views/Home/ShowEnvelope.cshtml | 3 +- .../{Home => TestView}/DebugEnvelopes.cshtml | 0 .../Views/{Home => TestView}/Index.cshtml | 0 EnvelopeGenerator.Web/appsettings.json | 16 +- EnvelopeGenerator.Web/wwwroot/js/app.js | 5 +- 47 files changed, 642 insertions(+), 308 deletions(-) create mode 100644 EnvelopeGenerator.Application/EnvelopeFlag.cs create mode 100644 EnvelopeGenerator.Application/EnvelopeGeneratorExtensions.cs create mode 100644 EnvelopeGenerator.Application/MessageKey.cs delete mode 100644 EnvelopeGenerator.Domain/Constants.cs create mode 100644 EnvelopeGenerator.Web/Controllers/Test/TestViewController.cs create mode 100644 EnvelopeGenerator.Web/MessageKey.cs rename EnvelopeGenerator.Web/Views/{Home => TestView}/DebugEnvelopes.cshtml (100%) rename EnvelopeGenerator.Web/Views/{Home => TestView}/Index.cshtml (100%) 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) - .Select(er => er.AccessCode) - .FirstOrDefaultAsync(); + 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 accessCode; + 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(); } } \ 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) + + [HttpGet("/EnvelopeKey/{envelopeReceiverId}")] + public async Task SendAccessCode([FromRoute] string envelopeReceiverId) { + ViewData["EnvelopeKey"] = envelopeReceiverId; try { - var passwordFromConfig = _config["Config:AdminPassword"]; - - if (passwordFromConfig == null) - { - ViewData["error"] = "No admin password configured!"; - return View("Index"); + (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; + + 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); + } } - - if (password != passwordFromConfig) + else { - ViewData["error"] = "Wrong Password!"; - return View("Index"); + envelopeResult.WithMessageKey(MessageKey.FailedToSendAccessCode); + _logger.LogError($"{MessageKey.FailedToSendAccessCode.ToString()}"); } - - 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) - { - bool accessCodeAlreadyRequested = database.Models.receiverModel.AccessCodeAlreadyRequested(response.Receiver.Email, response.Envelope.Id); - - 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); - } + _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(); + try + { + (string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId(); - _logger.LogInformation($"Envelope UUID: [{decodedId.EnvelopeUuid}]"); - _logger.LogInformation($"Receiver Signature: [{decodedId.ReceiverSignature}]"); + if(uuid is null || signature is null) + { + _logger.LogWarning($"{MessageKey.WrongEnvelopeReceiverId.ToString()}"); + return BadRequest(_envelopeService.CreateMessage(false, MessageKey.WrongEnvelopeReceiverId.ToString())); + } - var verification = await _envRcvService.VerifyAccessCode(decodedId.EnvelopeUuid, access_code); - EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId); + _logger.LogInformation($"Envelope UUID: [{uuid}]\nReceiver Signature: [{signature}]"); - if (verification.IsSuccess) - { - if (envelopeOldService.ReceiverAlreadySigned(response.Envelope, response.Receiver.Id)) + 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) { - return Redirect("/EnvelopeKey/{envelopeReceiverId}/Success"); - } + _logger.LogServiceMessage(verification); - 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; + if (verification.HasFlag(Flag.SecurityBreach)) + return Forbid(); - if (response.Envelope.Documents.Count() > 0) + return StatusCode(StatusCodes.Status500InternalServerError, verification.ClientMessages.Join()); + } + else if (isVerified) { - var document = await envelopeOldService.GetDocument(response.Envelope.Documents[0].Id, envelopeReceiverId); - byte[] bytes = await envelopeOldService.GetDocumentContents(document); - ViewData["DocumentBytes"] = bytes; + 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), - }; - - var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); - var authProperties = new AuthenticationProperties { - }; - - await HttpContext.SignInAsync( - CookieAuthenticationDefaults.AuthenticationScheme, - new ClaimsPrincipal(claimsIdentity), - authProperties); + database.Services.actionService.EnterIncorrectAccessCode(response.Envelope, response.Receiver); //for history + _logger.LogWarning(string.Join("\n", verification.Messages)); + return Unauthorized(); - 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) From 966b7de3c4795436662b2fba087df0f90760a6a8 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 25 Apr 2024 16:17:10 +0200 Subject: [PATCH 2/3] =?UTF-8?q?ErrorView=20und=20ErrorViewModel=20hinzugef?= =?UTF-8?q?=C3=BCgt;=20ContactLink=20als=20Singleton=20konfiguriert,=20ser?= =?UTF-8?q?ialisiert=20aus=20appSettings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/HomeController.cs | 21 +- .../EnvelopeGenerator.Web.csproj | 4 + EnvelopeGenerator.Web/Models/ContactLink.cs | 60 +++ .../Models/ErrorViewModel.cs | 6 +- EnvelopeGenerator.Web/Program.cs | 7 +- .../Views/Home/EnvelopeLocked.cshtml | 14 +- .../Views/Home/ShowEnvelope.cshtml | 8 +- .../Views/Shared/Error.cshtml | 25 -- .../Views/Shared/_Error.cshtml | 69 ++++ EnvelopeGenerator.Web/Views/_ViewStart.cshtml | 2 +- EnvelopeGenerator.Web/appsettings.json | 9 +- .../wwwroot/css/error-space.css | 386 ++++++++++++++++++ EnvelopeGenerator.Web/wwwroot/error.html | 61 +++ EnvelopeGenerator.Web/wwwroot/js/app.js | 5 +- .../wwwroot/js/error-space.js | 77 ++++ 15 files changed, 695 insertions(+), 59 deletions(-) create mode 100644 EnvelopeGenerator.Web/Models/ContactLink.cs delete mode 100644 EnvelopeGenerator.Web/Views/Shared/Error.cshtml create mode 100644 EnvelopeGenerator.Web/Views/Shared/_Error.cshtml create mode 100644 EnvelopeGenerator.Web/wwwroot/css/error-space.css create mode 100644 EnvelopeGenerator.Web/wwwroot/error.html create mode 100644 EnvelopeGenerator.Web/wwwroot/js/error-space.js diff --git a/EnvelopeGenerator.Web/Controllers/HomeController.cs b/EnvelopeGenerator.Web/Controllers/HomeController.cs index cb82faa9..b6275548 100644 --- a/EnvelopeGenerator.Web/Controllers/HomeController.cs +++ b/EnvelopeGenerator.Web/Controllers/HomeController.cs @@ -6,7 +6,6 @@ using EnvelopeGenerator.Web.Services; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc; -using System.Diagnostics; using System.Security.Claims; using Microsoft.AspNetCore.Authorization; using DigitalData.Core.API; @@ -17,16 +16,14 @@ namespace EnvelopeGenerator.Web.Controllers public class HomeController : BaseController { private readonly EnvelopeOldService envelopeOldService; - private readonly IConfiguration _config; private readonly IEnvelopeReceiverService _envRcvService; private readonly IEnvelopeService _envelopeService; private readonly IEnvelopeHistoryService _historyService; - public HomeController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger logger, IConfiguration configuration, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeService envelopeService, IEnvelopeHistoryService historyService) : base(databaseService, logger) + public HomeController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeService envelopeService, IEnvelopeHistoryService historyService) : base(databaseService, logger) { this.envelopeOldService = envelopeOldService; _envRcvService = envelopeReceiverService; _envelopeService = envelopeService; - _config = configuration; _historyService = historyService; } @@ -68,12 +65,22 @@ namespace EnvelopeGenerator.Web.Controllers _logger.LogError(ex, MessageKey.UnexpectedError.ToString()); } - return View("EnvelopeLocked").WithData("EnvelopeKey", envelopeReceiverId); + return Redirect($"{envelopeReceiverId}/Locked"); } [HttpGet("EnvelopeKey/{envelopeReceiverId}/Locked")] public IActionResult EnvelopeLocked([FromRoute] string envelopeReceiverId) { + (string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId(); + if (uuid is null || signature is null) + { + return View("_Error", new ErrorViewModel() + { + Title = "404", + Subtitle = "Anfrage fehlgeschlagen!", + Body = "Das angeforderte Umschlag wurde nicht gefunden." + }); + } return View().WithData("EnvelopeKey", envelopeReceiverId); } @@ -87,7 +94,7 @@ namespace EnvelopeGenerator.Web.Controllers if(uuid is null || signature is null) { _logger.LogWarning($"{MessageKey.WrongEnvelopeReceiverId.ToString()}"); - return BadRequest(_envelopeService.CreateMessage(false, MessageKey.WrongEnvelopeReceiverId.ToString())); + return BadRequest(_envelopeService.CreateMessage(false, MessageKey.WrongEnvelopeReceiverId.ToString())); } _logger.LogInformation($"Envelope UUID: [{uuid}]\nReceiver Signature: [{signature}]"); @@ -182,7 +189,7 @@ namespace EnvelopeGenerator.Web.Controllers [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { - return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); + return View(); } } } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj b/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj index 7cd5767a..4df31124 100644 --- a/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj +++ b/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj @@ -6,6 +6,10 @@ enable + + + + diff --git a/EnvelopeGenerator.Web/Models/ContactLink.cs b/EnvelopeGenerator.Web/Models/ContactLink.cs new file mode 100644 index 00000000..8221d4b4 --- /dev/null +++ b/EnvelopeGenerator.Web/Models/ContactLink.cs @@ -0,0 +1,60 @@ +namespace EnvelopeGenerator.Web.Models +{ + /// + /// Represents a hyperlink for contact purposes with various HTML attributes. + /// + public class ContactLink + { + /// + /// Gets or sets the label of the hyperlink. + /// + public string Label { get; init; } = "Contact"; + + /// + /// Gets or sets the URL that the hyperlink points to. + /// + public string Href { get; set; } = string.Empty; + + /// + /// Gets or sets the target where the hyperlink should open. + /// Commonly used values are "_blank", "_self", "_parent", "_top". + /// + public string Target { get; set; } = "_blank"; + + /// + /// Gets or sets the relationship of the linked URL as space-separated link types. + /// Examples include "nofollow", "noopener", "noreferrer". + /// + public string Rel { get; set; } = string.Empty; + + /// + /// Gets or sets the filename that should be downloaded when clicking the hyperlink. + /// This attribute will only have an effect if the href attribute is set. + /// + public string Download { get; set; } = string.Empty; + + /// + /// Gets or sets the language of the linked resource. Useful when linking to + /// content in another language. + /// + public string HrefLang { get; set; } = "en"; + + /// + /// Gets or sets the MIME type of the linked URL. Helps browsers to handle + /// the type correctly when the link is clicked. + /// + public string Type { get; set; } = string.Empty; + + /// + /// Gets or sets additional information about the hyperlink, typically viewed + /// as a tooltip when the mouse hovers over the link. + /// + public string Title { get; set; } = string.Empty; + + /// + /// Gets or sets an identifier for the hyperlink, unique within the HTML document. + /// + public string Id { get; set; } = string.Empty; + } + +} diff --git a/EnvelopeGenerator.Web/Models/ErrorViewModel.cs b/EnvelopeGenerator.Web/Models/ErrorViewModel.cs index a4489520..2173e705 100644 --- a/EnvelopeGenerator.Web/Models/ErrorViewModel.cs +++ b/EnvelopeGenerator.Web/Models/ErrorViewModel.cs @@ -2,8 +2,10 @@ namespace EnvelopeGenerator.Web.Models { public class ErrorViewModel { - public string? RequestId { get; set; } + public string Title { get; init; } = "404"; - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + public string Subtitle { get; init; } = "Hmmm..."; + + public string Body { get; init; } = "It looks like one of the developers fell asleep"; } } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Program.cs b/EnvelopeGenerator.Web/Program.cs index dae95eb4..0d424845 100644 --- a/EnvelopeGenerator.Web/Program.cs +++ b/EnvelopeGenerator.Web/Program.cs @@ -14,6 +14,7 @@ using DigitalData.Core.API; using Microsoft.AspNetCore.Authentication.Cookies; using DigitalData.Core.Application; using DigitalData.UserManager.Application.MappingProfiles; +using EnvelopeGenerator.Web.Models; var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); logger.Info("Logging initialized!"); @@ -105,10 +106,10 @@ try builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { - options.Cookie.HttpOnly = true; // Makes the cookie inaccessible to client-side scripts for security + options.Cookie.HttpOnly = true; // Makes the cookie inaccessible to client-side scripts for security options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; // Ensures cookies are sent over HTTPS only options.Cookie.SameSite = SameSiteMode.Strict; // Protects against CSRF attacks by restricting how cookies are sent with requests from external sites - // Set up event handlers for dynamic login and logout paths + options.Events = new CookieAuthenticationEvents { OnRedirectToLogin = context => @@ -132,6 +133,8 @@ try }; }); + builder.Services.AddSingleton(_ => builder.Configuration.GetSection("ContactLink").Get() ?? new ContactLink()); + builder.Services.AddCookieConsentSettings(); var app = builder.Build(); diff --git a/EnvelopeGenerator.Web/Views/Home/EnvelopeLocked.cshtml b/EnvelopeGenerator.Web/Views/Home/EnvelopeLocked.cshtml index 0e824a25..c3de39c5 100644 --- a/EnvelopeGenerator.Web/Views/Home/EnvelopeLocked.cshtml +++ b/EnvelopeGenerator.Web/Views/Home/EnvelopeLocked.cshtml @@ -1,18 +1,6 @@ @{ ViewData["Title"] = "Dokument geschützt"; } -@if(ViewData["Test1"] is string test1) -{ - -} -@if (ViewData["Test2"] is string test2) -{ - -}
@@ -32,7 +20,7 @@
- +
diff --git a/EnvelopeGenerator.Web/Views/Home/ShowEnvelope.cshtml b/EnvelopeGenerator.Web/Views/Home/ShowEnvelope.cshtml index 613fba3e..7b339119 100644 --- a/EnvelopeGenerator.Web/Views/Home/ShowEnvelope.cshtml +++ b/EnvelopeGenerator.Web/Views/Home/ShowEnvelope.cshtml @@ -38,7 +38,7 @@
@($"{envelope.Title}")

@($"Sie haben {(pages.Count())} Briefe zu unterschreiben. Bitte prüfen Sie die Seiten {stPageIndexes}.")

-

Erstellt am @envelope.AddedWhen

+

Erstellt am @envelope.AddedWhen von @sender?.Prename @sender?.Name. Sie können den Absender über @sender?.Email kontaktieren.

@@ -63,9 +63,9 @@ { var envelopeResponse = ViewData["EnvelopeResponse"]; var settings = new Newtonsoft.Json.JsonSerializerSettings - { - ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() - }; + { + ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() + }; var envelopeResponseJson = Newtonsoft.Json.JsonConvert.SerializeObject(envelopeResponse, settings); var documentBase64String = Convert.ToBase64String(documentBytes); diff --git a/EnvelopeGenerator.Web/Views/Shared/Error.cshtml b/EnvelopeGenerator.Web/Views/Shared/Error.cshtml deleted file mode 100644 index a1e04783..00000000 --- a/EnvelopeGenerator.Web/Views/Shared/Error.cshtml +++ /dev/null @@ -1,25 +0,0 @@ -@model ErrorViewModel -@{ - ViewData["Title"] = "Error"; -} - -

Error.

-

An error occurred while processing your request.

- -@if (Model.ShowRequestId) -{ -

- Request ID: @Model.RequestId -

-} - -

Development Mode

-

- Swapping to Development environment will display more detailed information about the error that occurred. -

-

- The Development environment shouldn't be enabled for deployed applications. - It can result in displaying sensitive information from exceptions to end users. - For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development - and restarting the app. -

diff --git a/EnvelopeGenerator.Web/Views/Shared/_Error.cshtml b/EnvelopeGenerator.Web/Views/Shared/_Error.cshtml new file mode 100644 index 00000000..f72914f1 --- /dev/null +++ b/EnvelopeGenerator.Web/Views/Shared/_Error.cshtml @@ -0,0 +1,69 @@ +@{ + Layout = null; +} +@model ErrorViewModel +@{ + var errorModel = Model ?? new ErrorViewModel(); +} +@inject ContactLink _contactLink + + + + + + + Document + + + + +
+
+
+
+ +
+
+
+
+
+ +
+
@errorModel.Title
+
@errorModel.Subtitle
+
@errorModel.Body
+ @_contactLink.Label +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ +
+ +
+
+
+
+ + + + + \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Views/_ViewStart.cshtml b/EnvelopeGenerator.Web/Views/_ViewStart.cshtml index a5f10045..1af6e494 100644 --- a/EnvelopeGenerator.Web/Views/_ViewStart.cshtml +++ b/EnvelopeGenerator.Web/Views/_ViewStart.cshtml @@ -1,3 +1,3 @@ @{ Layout = "_Layout"; -} +} \ No newline at end of file diff --git a/EnvelopeGenerator.Web/appsettings.json b/EnvelopeGenerator.Web/appsettings.json index fd26c28d..5dcb02dc 100644 --- a/EnvelopeGenerator.Web/appsettings.json +++ b/EnvelopeGenerator.Web/appsettings.json @@ -28,7 +28,7 @@ "maxArchiveDays": 30 } }, - // Trace, Debug, Info, Warn, Error and Fatal + // Trace, Debug, Info, Warn, Error and *Fatal* "rules": [ { "logger": "*", @@ -80,5 +80,12 @@ "ModalId": "bootstrapCookieConsentSettingsModal", "AlsoUseLocalStorage": false, "Categories": [ "necessary" ] + }, + "ContactLink": { + "Label": "Kontakt", + "Href": "https://digitaldata.works/", + "HrefLang": "de", + "Target": "_blank", + "Title": "Digital Data GmbH" } } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/wwwroot/css/error-space.css b/EnvelopeGenerator.Web/wwwroot/css/error-space.css new file mode 100644 index 00000000..1cf74752 --- /dev/null +++ b/EnvelopeGenerator.Web/wwwroot/css/error-space.css @@ -0,0 +1,386 @@ +html, +body { + height: 100%; + width: 100%; + margin: 0px; + background: linear-gradient(90deg, rgba(47, 54, 64, 1) 23%, rgba(24, 27, 32, 1) 100%); +} + +.moon { + background: linear-gradient(90deg, rgba(208, 208, 208, 1) 48%, rgba(145, 145, 145, 1) 100%); + position: absolute; + top: -100px; + left: -300px; + width: 900px; + height: 900px; + content: ''; + border-radius: 100%; + box-shadow: 0px 0px 30px -4px rgba(0, 0, 0, 0.5); +} + +.moon__crater { + position: absolute; + content: ''; + border-radius: 100%; + background: linear-gradient(90deg, rgba(122, 122, 122, 1) 38%, rgba(195, 195, 195, 1) 100%); + opacity: 0.6; +} + +.moon__crater1 { + top: 250px; + left: 500px; + width: 60px; + height: 180px; +} + +.moon__crater2 { + top: 650px; + left: 340px; + width: 40px; + height: 80px; + transform: rotate(55deg); +} + +.moon__crater3 { + top: -20px; + left: 40px; + width: 65px; + height: 120px; + transform: rotate(250deg); +} + +.star { + background: grey; + position: absolute; + width: 5px; + height: 5px; + content: ''; + border-radius: 100%; + transform: rotate(250deg); + opacity: 0.4; + animation-name: shimmer; + animation-duration: 1.5s; + animation-iteration-count: infinite; + animation-direction: alternate; +} + +@keyframes shimmer { + from { + opacity: 0; + } + + to { + opacity: 0.7; + } +} + +.star1 { + top: 40%; + left: 50%; + animation-delay: 1s; +} + +.star2 { + top: 60%; + left: 90%; + animation-delay: 3s; +} + +.star3 { + top: 10%; + left: 70%; + animation-delay: 2s; +} + +.star4 { + top: 90%; + left: 40%; +} + +.star5 { + top: 20%; + left: 30%; + animation-delay: 0.5s; +} + +.error { + position: absolute; + left: 100px; + top: 400px; + transform: translateY(-60%); + font-family: 'Montserrat', sans-serif; + color: #363e49; +} + +.error__title { + font-size: 10em; +} + +.error__subtitle { + font-size: 2em; +} + +.error__description { + opacity: 0.5; +} + +.error__button { + display: inline-block; + margin-top: 3em; + margin-right: 0.5em; + padding: 0.5em 2em; + outline: none; + border: 2px solid #2f3640; + background-color: transparent; + border-radius: 8em; + color: #576375; + cursor: pointer; + transition-duration: 0.2s; + font-size: 0.75em; + font-family: 'Montserrat', sans-serif; + text-decoration: none; +} + +.error__button:hover { + color: #21252c; +} + +.error__button--active { + background-color: #e67e22; + border: 2px solid #e67e22; + color: white; +} + +.error__button--active:hover { + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.5); + color: white; +} + +.astronaut { + position: absolute; + width: 185px; + height: 300px; + left: 70%; + top: 50%; + transform: translate(-50%, -50%) rotate(20deg) scale(1.2); +} + +.astronaut__head { + background-color: white; + position: absolute; + top: 60px; + left: 60px; + width: 60px; + height: 60px; + content: ''; + border-radius: 2em; +} + +.astronaut__head-visor-flare1 { + background-color: #7f8fa6; + position: absolute; + top: 28px; + left: 40px; + width: 10px; + height: 10px; + content: ''; + border-radius: 2em; + opacity: 0.5; +} + +.astronaut__head-visor-flare2 { + background-color: #718093; + position: absolute; + top: 40px; + left: 38px; + width: 5px; + height: 5px; + content: ''; + border-radius: 2em; + opacity: 0.3; +} + +.astronaut__backpack { + background-color: #bfbfbf; + position: absolute; + top: 90px; + left: 47px; + width: 86px; + height: 90px; + content: ''; + border-radius: 8px; +} + +.astronaut__body { + background-color: #e6e6e6; + position: absolute; + top: 115px; + left: 55px; + width: 70px; + height: 80px; + content: ''; + border-radius: 8px; +} + +.astronaut__body__chest { + background-color: #d9d9d9; + position: absolute; + top: 140px; + left: 68px; + width: 45px; + height: 25px; + content: ''; + border-radius: 6px; +} + +.astronaut__arm-left1 { + background-color: #e6e6e6; + position: absolute; + top: 127px; + left: 9px; + width: 65px; + height: 20px; + content: ''; + border-radius: 8px; + transform: rotate(-30deg); +} + +.astronaut__arm-left2 { + background-color: #e6e6e6; + position: absolute; + top: 102px; + left: 7px; + width: 20px; + height: 45px; + content: ''; + border-radius: 8px; + transform: rotate(-12deg); + border-top-left-radius: 8em; + border-top-right-radius: 8em; +} + +.astronaut__arm-right1 { + background-color: #e6e6e6; + position: absolute; + top: 113px; + left: 100px; + width: 65px; + height: 20px; + content: ''; + border-radius: 8px; + transform: rotate(-10deg); +} + +.astronaut__arm-right2 { + background-color: #e6e6e6; + position: absolute; + top: 78px; + left: 141px; + width: 20px; + height: 45px; + content: ''; + border-radius: 8px; + transform: rotate(-10deg); + border-top-left-radius: 8em; + border-top-right-radius: 8em; +} + +.astronaut__arm-thumb-left { + background-color: #e6e6e6; + position: absolute; + top: 110px; + left: 21px; + width: 10px; + height: 6px; + content: ''; + border-radius: 8em; + transform: rotate(-35deg); +} + +.astronaut__arm-thumb-right { + background-color: #e6e6e6; + position: absolute; + top: 90px; + left: 133px; + width: 10px; + height: 6px; + content: ''; + border-radius: 8em; + transform: rotate(20deg); +} + +.astronaut__wrist-left { + background-color: #e67e22; + position: absolute; + top: 122px; + left: 6.5px; + width: 21px; + height: 4px; + content: ''; + border-radius: 8em; + transform: rotate(-15deg); +} + +.astronaut__wrist-right { + background-color: #e67e22; + position: absolute; + top: 98px; + left: 141px; + width: 21px; + height: 4px; + content: ''; + border-radius: 8em; + transform: rotate(-10deg); +} + +.astronaut__leg-left { + background-color: #e6e6e6; + position: absolute; + top: 188px; + left: 50px; + width: 23px; + height: 75px; + content: ''; + transform: rotate(10deg); +} + +.astronaut__leg-right { + background-color: #e6e6e6; + position: absolute; + top: 188px; + left: 108px; + width: 23px; + height: 75px; + content: ''; + transform: rotate(-10deg); +} + +.astronaut__foot-left { + background-color: white; + position: absolute; + top: 240px; + left: 43px; + width: 28px; + height: 20px; + content: ''; + transform: rotate(10deg); + border-radius: 3px; + border-top-left-radius: 8em; + border-top-right-radius: 8em; + border-bottom: 4px solid #e67e22; +} + +.astronaut__foot-right { + background-color: white; + position: absolute; + top: 240px; + left: 111px; + width: 28px; + height: 20px; + content: ''; + transform: rotate(-10deg); + border-radius: 3px; + border-top-left-radius: 8em; + border-top-right-radius: 8em; + border-bottom: 4px solid #e67e22; +} \ No newline at end of file diff --git a/EnvelopeGenerator.Web/wwwroot/error.html b/EnvelopeGenerator.Web/wwwroot/error.html new file mode 100644 index 00000000..41ba16de --- /dev/null +++ b/EnvelopeGenerator.Web/wwwroot/error.html @@ -0,0 +1,61 @@ + + + + + + + Document + + + + +
+
+
+
+ +
+
+
+
+
+ +
+
404
+
Hmmm...
+
It looks like one of the developers fell asleep
+ CONTACT +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ +
+ +
+
+
+
+ + + + + \ No newline at end of file diff --git a/EnvelopeGenerator.Web/wwwroot/js/app.js b/EnvelopeGenerator.Web/wwwroot/js/app.js index a0e021c4..2756c48f 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/app.js +++ b/EnvelopeGenerator.Web/wwwroot/js/app.js @@ -34,7 +34,6 @@ class App { this.currentReceiver = this.envelopeResponse.receiver // Load the document from the filestore - console.debug('Loading document from filestore') const documentResponse = this.documentBytes if (documentResponse.fatal || documentResponse.error) { @@ -45,11 +44,9 @@ class App { icon: 'error', }) } - console.log(documentResponse.data) - console.log(this.documentBytes) const arrayBuffer = this.documentBytes - console.log(arrayBuffer) + // Load PSPDFKit this.Instance = await this.UI.loadPSPDFKit(arrayBuffer, this.container) this.UI.configurePSPDFKit(this.Instance, this.handleClick.bind(this)) diff --git a/EnvelopeGenerator.Web/wwwroot/js/error-space.js b/EnvelopeGenerator.Web/wwwroot/js/error-space.js new file mode 100644 index 00000000..80f1cae3 --- /dev/null +++ b/EnvelopeGenerator.Web/wwwroot/js/error-space.js @@ -0,0 +1,77 @@ +function drawVisor() { + const canvas = document.getElementById('visor'); + const ctx = canvas.getContext('2d'); + + ctx.beginPath(); + ctx.moveTo(5, 45); + ctx.bezierCurveTo(15, 64, 45, 64, 55, 45); + + ctx.lineTo(55, 20); + ctx.bezierCurveTo(55, 15, 50, 10, 45, 10); + + ctx.lineTo(15, 10); + + ctx.bezierCurveTo(15, 10, 5, 10, 5, 20); + ctx.lineTo(5, 45); + + ctx.fillStyle = '#2f3640'; + ctx.strokeStyle = '#f5f6fa'; + ctx.fill(); + ctx.stroke(); +} + +const cordCanvas = document.getElementById('cord'); +const ctx = cordCanvas.getContext('2d'); + +let y1 = 160; +let y2 = 100; +let y3 = 100; + +let y1Forward = true; +let y2Forward = false; +let y3Forward = true; + +function animate() { + requestAnimationFrame(animate); + ctx.clearRect(0, 0, innerWidth, innerHeight); + + ctx.beginPath(); + ctx.moveTo(130, 170); + ctx.bezierCurveTo(250, y1, 345, y2, 400, y3); + + ctx.strokeStyle = 'white'; + ctx.lineWidth = 8; + ctx.stroke(); + + + if (y1 === 100) { + y1Forward = true; + } + + if (y1 === 300) { + y1Forward = false; + } + + if (y2 === 100) { + y2Forward = true; + } + + if (y2 === 310) { + y2Forward = false; + } + + if (y3 === 100) { + y3Forward = true; + } + + if (y3 === 317) { + y3Forward = false; + } + + y1Forward ? y1 += 1 : y1 -= 1; + y2Forward ? y2 += 1 : y2 -= 1; + y3Forward ? y3 += 1 : y3 -= 1; +} + +drawVisor(); +animate(); \ No newline at end of file From 6b3c90c618a7e9fe41855fa6d5817621bd3a2bfe Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Fri, 26 Apr 2024 12:22:09 +0200 Subject: [PATCH 3/3] =?UTF-8?q?Benutzerdefinierte=20Fehlerseiten=20f=C3=BC?= =?UTF-8?q?r=20die=20Statuscodes=20404=20und=20500=20im=20HomeController?= =?UTF-8?q?=20hinzugef=C3=BCgt,=20um=20verschiedene=20Benutzerf=C3=A4lle?= =?UTF-8?q?=20zu=20behandeln.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Contracts/IEnvelopeReceiverService.cs | 2 + EnvelopeGenerator.Application/EnvelopeFlag.cs | 3 +- .../EnvelopeGeneratorExtensions.cs | 18 ++-- .../Services/EnvelopeReceiverService.cs | 11 ++ .../Contracts/IEnvelopeReceiverRepository.cs | 2 + .../Repositories/EnvlopeReceiverRepository.cs | 7 +- .../Controllers/ControllerBaseExtensions.cs | 26 ++++- .../Controllers/HomeController.cs | 99 +++++++++++------- .../EnvelopeGenerator.Web.csproj | 3 + EnvelopeGenerator.Web/MessageKey.cs | 4 +- EnvelopeGenerator.Web/Program.cs | 2 +- .../wwwroot/css/error-space.css | 13 ++- EnvelopeGenerator.Web/wwwroot/favicon.ico | Bin 5430 -> 270398 bytes 13 files changed, 138 insertions(+), 52 deletions(-) diff --git a/EnvelopeGenerator.Application/Contracts/IEnvelopeReceiverService.cs b/EnvelopeGenerator.Application/Contracts/IEnvelopeReceiverService.cs index 58b246ed..59d9c595 100644 --- a/EnvelopeGenerator.Application/Contracts/IEnvelopeReceiverService.cs +++ b/EnvelopeGenerator.Application/Contracts/IEnvelopeReceiverService.cs @@ -19,5 +19,7 @@ namespace EnvelopeGenerator.Application.Contracts Task> VerifyAccessCodeAsync(string uuid, string signature, string accessCode); Task> VerifyAccessCodeAsync(string envelopeReceiverId, string accessCode); + + Task> IsExisting(string envelopeReceiverId); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/EnvelopeFlag.cs b/EnvelopeGenerator.Application/EnvelopeFlag.cs index 516575ae..705df692 100644 --- a/EnvelopeGenerator.Application/EnvelopeFlag.cs +++ b/EnvelopeGenerator.Application/EnvelopeFlag.cs @@ -2,6 +2,7 @@ { public enum EnvelopeFlag { - EnvelopeOrReceiverNonexists + EnvelopeOrReceiverNonexists, + NonDecodableEnvelopeReceiverId } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/EnvelopeGeneratorExtensions.cs b/EnvelopeGenerator.Application/EnvelopeGeneratorExtensions.cs index 6d7ad6f9..43cf6390 100644 --- a/EnvelopeGenerator.Application/EnvelopeGeneratorExtensions.cs +++ b/EnvelopeGenerator.Application/EnvelopeGeneratorExtensions.cs @@ -6,19 +6,20 @@ namespace EnvelopeGenerator.Application { public static class EnvelopeGeneratorExtensions { - public static void LogEnvelopeError(this ILogger logger, string receiverId, string? message, params object?[] args) + public static void LogEnvelopeError(this ILogger logger, string envelopeEeceiverId, Exception? exception = null, string? message = null, params object?[] args) { - (string? envelopeUuid, string? receiverSignature) = receiverId.DecodeEnvelopeReceiverId(); - - var sb = new StringBuilder($"Envelope Uuid: {envelopeUuid}\nReceiver Signature: {receiverSignature}"); + var sb = new StringBuilder(envelopeEeceiverId.DecodeEnvelopeReceiverId().ToTitle()); if (message is not null) sb.AppendLine().Append(message); - logger.Log(LogLevel.Error, sb.ToString(), args); + if(exception is null) + logger.Log(LogLevel.Error, sb.ToString(), args); + else + logger.Log(LogLevel.Error, exception, sb.ToString(), args); } - public static void LogEnvelopeError(this ILogger logger, string uuid, string? signature = null, string? message = null, params object?[] args) + public static void LogEnvelopeError(this ILogger logger, string? uuid, string? signature = null, Exception? exception = null, string? message = null, params object?[] args) { var sb = new StringBuilder($"Envelope Uuid: {uuid}"); @@ -28,7 +29,10 @@ namespace EnvelopeGenerator.Application if (message is not null) sb.AppendLine().Append(message); - logger.Log(LogLevel.Error, sb.ToString(), args); + if (exception is null) + logger.Log(LogLevel.Error, sb.ToString(), args); + else + logger.Log(LogLevel.Error, exception, sb.ToString(), args); } public static string ToTitle(this (string? UUID, string? Signature) envelopeReceiverTuple) diff --git a/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs b/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs index bea643f2..cdfa33ed 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs @@ -95,5 +95,16 @@ namespace EnvelopeGenerator.Application.Services return await VerifyAccessCodeAsync(uuid: uuid, signature: signature, accessCode: accessCode); } + + public async Task> IsExisting(string envelopeReceiverId) + { + (string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId(); + + if (uuid is null || signature is null) + return Failed(false).WithFlag(EnvelopeFlag.NonDecodableEnvelopeReceiverId); + + int count = await _repository.CountAsync(uuid:uuid, signature:signature); + return Successful(count > 0); + } } } \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeReceiverRepository.cs b/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeReceiverRepository.cs index ba7e7de2..8f6b15dc 100644 --- a/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeReceiverRepository.cs +++ b/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeReceiverRepository.cs @@ -12,5 +12,7 @@ namespace EnvelopeGenerator.Infrastructure.Contracts Task ReadByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true); Task ReadAccessCodeAsync(string uuid, string signature); + + Task CountAsync(string uuid, string signature); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/Repositories/EnvlopeReceiverRepository.cs b/EnvelopeGenerator.Infrastructure/Repositories/EnvlopeReceiverRepository.cs index 6fb35346..b19ee540 100644 --- a/EnvelopeGenerator.Infrastructure/Repositories/EnvlopeReceiverRepository.cs +++ b/EnvelopeGenerator.Infrastructure/Repositories/EnvlopeReceiverRepository.cs @@ -24,11 +24,12 @@ namespace EnvelopeGenerator.Infrastructure.Repositories query = query.Where(er => er.Receiver != null && er.Receiver.Signature == signature); if (withEnvelope) - query = query.Include(er => er.Envelope); + query = query.Include(er => er.Envelope).ThenInclude(e => e!.Documents!).ThenInclude(d => d.Elements) + .Include(er => er.Envelope).ThenInclude(e => e!.History) + .Include(er => er.Envelope).ThenInclude(e => e!.History); if (withReceiver) query = query.Include(er => er.Receiver); - return query; } @@ -45,5 +46,7 @@ namespace EnvelopeGenerator.Infrastructure.Repositories => await ReadWhere(uuid:uuid, signature:signature) .Select(er => er.AccessCode) .FirstOrDefaultAsync(); + + public async Task CountAsync(string uuid, string signature) => await ReadWhere(uuid: uuid, signature: signature).CountAsync(); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Controllers/ControllerBaseExtensions.cs b/EnvelopeGenerator.Web/Controllers/ControllerBaseExtensions.cs index be73e434..c6f5837f 100644 --- a/EnvelopeGenerator.Web/Controllers/ControllerBaseExtensions.cs +++ b/EnvelopeGenerator.Web/Controllers/ControllerBaseExtensions.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using EnvelopeGenerator.Web.Models; +using Microsoft.AspNetCore.Mvc; using System.Security.Claims; namespace EnvelopeGenerator.Web.Controllers @@ -38,5 +39,28 @@ namespace EnvelopeGenerator.Web.Controllers } return null; } + + public static ViewResult ViewError(this Controller controller, ErrorViewModel errorViewModel) => controller.View("_Error", errorViewModel); + + public static ViewResult ViewError404(this Controller controller) => controller.ViewError(new ErrorViewModel() + { + Title = "404", + Subtitle = "Die von Ihnen gesuchte Seite ist nicht verfügbar", + Body = "Sie können derzeit nur an Sie gerichtete Briefe einsehen und unterschreiben.", + }); + + public static ViewResult ViewEnvelopeNotFound(this Controller controller) => controller.ViewError(new ErrorViewModel() + { + Title = "404", + Subtitle = "Umschlag nicht gefunden", + Body = "Wenn Sie diese URL in Ihrer E-Mail erhalten haben, wenden Sie sich bitte an das IT-Team." + }); + + public static ViewResult ViewInnerServiceError(this Controller controller) => controller.ViewError(new ErrorViewModel() + { + Title = "500", + Subtitle = "Ein unerwarteter Fehler ist aufgetreten", + Body = "Bitte kontaktieren Sie das IT-Team." + }); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Controllers/HomeController.cs b/EnvelopeGenerator.Web/Controllers/HomeController.cs index b6275548..bd8d544d 100644 --- a/EnvelopeGenerator.Web/Controllers/HomeController.cs +++ b/EnvelopeGenerator.Web/Controllers/HomeController.cs @@ -1,7 +1,6 @@ using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.Services; using EnvelopeGenerator.Common; -using EnvelopeGenerator.Web.Models; using EnvelopeGenerator.Web.Services; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication; @@ -10,6 +9,10 @@ using System.Security.Claims; using Microsoft.AspNetCore.Authorization; using DigitalData.Core.API; using DigitalData.Core.Application; +using EnvelopeGenerator.Application; +using DigitalData.Core.Contracts.CultureServices; +using DigitalData.Core.CultureServices; +using Azure; namespace EnvelopeGenerator.Web.Controllers { @@ -19,12 +22,15 @@ namespace EnvelopeGenerator.Web.Controllers private readonly IEnvelopeReceiverService _envRcvService; private readonly IEnvelopeService _envelopeService; private readonly IEnvelopeHistoryService _historyService; - public HomeController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeService envelopeService, IEnvelopeHistoryService historyService) : base(databaseService, logger) + private readonly IKeyTranslationService _translator; + + public HomeController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeService envelopeService, IEnvelopeHistoryService historyService, IKeyTranslationService keyTranslationService) : base(databaseService, logger) { this.envelopeOldService = envelopeOldService; _envRcvService = envelopeReceiverService; _envelopeService = envelopeService; _historyService = historyService; + _translator = keyTranslationService; } [HttpGet("/EnvelopeKey/{envelopeReceiverId}")] @@ -33,15 +39,18 @@ namespace EnvelopeGenerator.Web.Controllers ViewData["EnvelopeKey"] = envelopeReceiverId; try { - (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; - - ViewData["EnvelopeResult"] = envelopeResult; - if(envelopeResult.IsSuccess && envelope is not null && mailAddress is not null && (envelope.UseAccessCode ?? false)) + var erResult = await _envRcvService.ReadByEnvelopeReceiverIdAsync(envelopeReceiverId: envelopeReceiverId); + var er = erResult.Data; + var receiver = er?.Receiver; + var envelope = er?.Envelope; + var mailAddress = receiver?.EmailAddress; + + if (erResult is null) + { + _logger.LogError(MessageKey.ServiceOutputNullError.TranslateWith(_translator)); + return this.ViewEnvelopeNotFound(); + } + else if (erResult.IsSuccess && mailAddress is not null && (envelope?.UseAccessCode ?? false)) { EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId); @@ -56,32 +65,37 @@ namespace EnvelopeGenerator.Web.Controllers } else { - envelopeResult.WithMessageKey(MessageKey.FailedToSendAccessCode); - _logger.LogError($"{MessageKey.FailedToSendAccessCode.ToString()}"); + _logger.LogServiceMessage(erResult); + return this.ViewEnvelopeNotFound(); } } catch(Exception ex) { - _logger.LogError(ex, MessageKey.UnexpectedError.ToString()); + _logger.LogEnvelopeError(envelopeEeceiverId: envelopeReceiverId, exception:ex, message: MessageKey.UnexpectedError.TranslateWith(_translator)); + return this.ViewInnerServiceError(); } return Redirect($"{envelopeReceiverId}/Locked"); } [HttpGet("EnvelopeKey/{envelopeReceiverId}/Locked")] - public IActionResult EnvelopeLocked([FromRoute] string envelopeReceiverId) + public async Task EnvelopeLocked([FromRoute] string envelopeReceiverId) { - (string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId(); - if (uuid is null || signature is null) + try { - return View("_Error", new ErrorViewModel() - { - Title = "404", - Subtitle = "Anfrage fehlgeschlagen!", - Body = "Das angeforderte Umschlag wurde nicht gefunden." - }); + var result = await _envRcvService.IsExisting(envelopeReceiverId: envelopeReceiverId); + bool isExisting = result.Data; + + if (result.HasFlag(EnvelopeFlag.NonDecodableEnvelopeReceiverId) || !isExisting) + return this.ViewEnvelopeNotFound(); + + return View().WithData("EnvelopeKey", envelopeReceiverId); + } + catch(Exception ex) + { + _logger.LogEnvelopeError(envelopeEeceiverId: envelopeReceiverId, exception: ex); + return this.ViewInnerServiceError(); } - return View().WithData("EnvelopeKey", envelopeReceiverId); } [HttpPost("/EnvelopeKey/{envelopeReceiverId}/Locked")] @@ -93,7 +107,7 @@ namespace EnvelopeGenerator.Web.Controllers if(uuid is null || signature is null) { - _logger.LogWarning($"{MessageKey.WrongEnvelopeReceiverId.ToString()}"); + _logger.LogEnvelopeError(uuid: uuid, signature: signature, message: MessageKey.WrongEnvelopeReceiverId.TranslateWith(_translator)); return BadRequest(_envelopeService.CreateMessage(false, MessageKey.WrongEnvelopeReceiverId.ToString())); } @@ -117,7 +131,7 @@ namespace EnvelopeGenerator.Web.Controllers { if (envelopeOldService.ReceiverAlreadySigned(response.Envelope, response.Receiver.Id)) { - return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Success"); + return View("EnvelopeSigned"); } var envelope = await _envelopeService.ReadByUuidAsync(uuid: uuid, signature: signature, withAll: true); @@ -171,9 +185,29 @@ namespace EnvelopeGenerator.Web.Controllers [HttpGet("/EnvelopeKey/{envelopeReceiverId}/Success")] public async Task EnvelopeSigned(string envelopeReceiverId) { - await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); - ViewData["EnvelopeKey"] = envelopeReceiverId; - return View(); + try + { + var result = await _envRcvService.IsExisting(envelopeReceiverId: envelopeReceiverId); + bool isExisting = result.Data; + if (result.HasFlag(EnvelopeFlag.NonDecodableEnvelopeReceiverId) || !isExisting) + return this.ViewEnvelopeNotFound(); + + EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId); + + if (!envelopeOldService.ReceiverAlreadySigned(response.Envelope, response.Receiver.Id)) + { + return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Locked"); + } + + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + ViewData["EnvelopeKey"] = envelopeReceiverId; + return View(); + } + catch (Exception ex) + { + _logger.LogEnvelopeError(envelopeEeceiverId: envelopeReceiverId, exception: ex); + return this.ViewInnerServiceError(); + } } [Authorize] @@ -185,11 +219,6 @@ 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(); - } + public IActionResult Error404() => this.ViewError404(); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj b/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj index 4df31124..220e8fc6 100644 --- a/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj +++ b/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj @@ -94,6 +94,9 @@ Always + + PreserveNewest + Always diff --git a/EnvelopeGenerator.Web/MessageKey.cs b/EnvelopeGenerator.Web/MessageKey.cs index 47ccc718..a1b2f6de 100644 --- a/EnvelopeGenerator.Web/MessageKey.cs +++ b/EnvelopeGenerator.Web/MessageKey.cs @@ -2,9 +2,11 @@ { public enum MessageKey { + ServiceOutputNullError, UnexpectedError, FailedToSendAccessCode, WrongEnvelopeReceiverId, //the value should be about URL (like URL is not existing) as a part of security. - DataIntegrityError + DataIntegrityError, + NonDecodableEnvelopeReceiverId } } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Program.cs b/EnvelopeGenerator.Web/Program.cs index 0d424845..84d28b61 100644 --- a/EnvelopeGenerator.Web/Program.cs +++ b/EnvelopeGenerator.Web/Program.cs @@ -160,7 +160,7 @@ try app.UseAuthorization(); app.MapControllers(); - + app.MapFallbackToController("Error404", "Home"); app.Run(); } catch(Exception ex) diff --git a/EnvelopeGenerator.Web/wwwroot/css/error-space.css b/EnvelopeGenerator.Web/wwwroot/css/error-space.css index 1cf74752..9a3391b0 100644 --- a/EnvelopeGenerator.Web/wwwroot/css/error-space.css +++ b/EnvelopeGenerator.Web/wwwroot/css/error-space.css @@ -6,11 +6,16 @@ body { background: linear-gradient(90deg, rgba(47, 54, 64, 1) 23%, rgba(24, 27, 32, 1) 100%); } +:root { + --moon-left-position: 0px; +} + + .moon { background: linear-gradient(90deg, rgba(208, 208, 208, 1) 48%, rgba(145, 145, 145, 1) 100%); position: absolute; top: -100px; - left: -300px; + left: var(--moon-left-position); width: 900px; height: 900px; content: ''; @@ -28,14 +33,14 @@ body { .moon__crater1 { top: 250px; - left: 500px; + left: calc(var(--moon-left-position) + 800px); width: 60px; height: 180px; } .moon__crater2 { top: 650px; - left: 340px; + left: calc(var(--moon-left-position) + 640px); width: 40px; height: 80px; transform: rotate(55deg); @@ -43,7 +48,7 @@ body { .moon__crater3 { top: -20px; - left: 40px; + left: calc(var(--moon-left-position) + 340px); width: 65px; height: 120px; transform: rotate(250deg); diff --git a/EnvelopeGenerator.Web/wwwroot/favicon.ico b/EnvelopeGenerator.Web/wwwroot/favicon.ico index 63e859b476eff5055e0e557aaa151ca8223fbeef..60c2c4ab511fbf7d85e5be3021ecfe159b57b40b 100644 GIT binary patch literal 270398 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