Compare commits
23 Commits
4f5b8f9d76
...
c41d5c4a76
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c41d5c4a76 | ||
|
|
27db664b4d | ||
|
|
ba2518cdd2 | ||
|
|
72a0cb78c7 | ||
|
|
e82d7552c2 | ||
|
|
4b50b6c35d | ||
|
|
103d8da6b2 | ||
|
|
15f3bd1bbd | ||
|
|
10a5adeeee | ||
|
|
3b5c6086a9 | ||
|
|
abda0d14e8 | ||
|
|
569ebc87cc | ||
|
|
6b6c8e407c | ||
|
|
556d02870e | ||
|
|
c6fc665002 | ||
|
|
030fd0e45b | ||
|
|
31e647d3e5 | ||
|
|
6dfdd48ec0 | ||
|
|
85cacc822d | ||
|
|
535ca23c86 | ||
|
|
7f1009e402 | ||
|
|
ea4b35f4b4 | ||
|
|
8e1b4e0832 |
@@ -4,7 +4,8 @@
|
|||||||
{
|
{
|
||||||
public string CharPool { get; init; } = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789012345678901234567890123456789";
|
public string CharPool { get; init; } = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789012345678901234567890123456789";
|
||||||
|
|
||||||
public int DefaultTotpSecretKeyLength { get; init; } = 32;
|
//TODO: Increase the DefaultTotpSecretKeyLength (e.g. to 32) but make sure that the QR code is generated correctly and can be scanned by the authenticator.
|
||||||
|
public int DefaultTotpSecretKeyLength { get; init; } = 20;
|
||||||
|
|
||||||
public string TotpIssuer { get; init; } = "signFlow";
|
public string TotpIssuer { get; init; } = "signFlow";
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ namespace EnvelopeGenerator.Application.Contracts
|
|||||||
{
|
{
|
||||||
public interface IEnvelopeMailService : IEmailOutService
|
public interface IEnvelopeMailService : IEmailOutService
|
||||||
{
|
{
|
||||||
Task<DataResult<int>> SendAsync(EnvelopeReceiverDto envelopeReceiverDto, Constants.EmailTemplateType tempType);
|
Task<DataResult<int>> SendAsync(EnvelopeReceiverDto envelopeReceiverDto, Constants.EmailTemplateType tempType, Dictionary<string, object>? optionalPlaceholders = null);
|
||||||
|
|
||||||
Task<DataResult<int>> SendAsync(EnvelopeReceiverReadOnlyDto dto);
|
Task<DataResult<int>> SendAsync(EnvelopeReceiverReadOnlyDto dto, Dictionary<string, object>? optionalPlaceholders = null);
|
||||||
|
|
||||||
Task<DataResult<int>> SendAccessCodeAsync(EnvelopeReceiverDto envelopeReceiverDto);
|
Task<DataResult<int>> SendAccessCodeAsync(EnvelopeReceiverDto envelopeReceiverDto);
|
||||||
|
|
||||||
|
Task<DataResult<int>> SendTFAQrCodeAsync(EnvelopeReceiverDto envelopeReceiverDto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using DigitalData.Core.Abstractions.Application;
|
using DigitalData.Core.Abstractions;
|
||||||
|
using DigitalData.Core.Abstractions.Application;
|
||||||
using DigitalData.Core.DTO;
|
using DigitalData.Core.DTO;
|
||||||
using EnvelopeGenerator.Application.DTOs.Receiver;
|
using EnvelopeGenerator.Application.DTOs.Receiver;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
@@ -7,8 +8,10 @@ namespace EnvelopeGenerator.Application.Contracts
|
|||||||
{
|
{
|
||||||
public interface IReceiverService : ICRUDService<ReceiverCreateDto, ReceiverReadDto, ReceiverUpdateDto, Receiver, int>
|
public interface IReceiverService : ICRUDService<ReceiverCreateDto, ReceiverReadDto, ReceiverUpdateDto, Receiver, int>
|
||||||
{
|
{
|
||||||
public Task<DataResult<ReceiverReadDto>> ReadByAsync(string? emailAddress = null, string? signature = null);
|
Task<DataResult<ReceiverReadDto>> ReadByAsync(string? emailAddress = null, string? signature = null);
|
||||||
|
|
||||||
public Task<Result> DeleteByAsync(string? emailAddress = null, string? signature = null);
|
Task<Result> DeleteByAsync(string? emailAddress = null, string? signature = null);
|
||||||
|
|
||||||
|
Task<Result> UpdateAsync<TUpdateDto>(TUpdateDto updateDto) where TUpdateDto : IUnique<int>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
using DigitalData.Core.DTO;
|
using DigitalData.Core.Abstractions;
|
||||||
|
using DigitalData.Core.DTO;
|
||||||
|
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
|
||||||
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
|
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
@@ -8,14 +10,17 @@ namespace EnvelopeGenerator.Application.DTOs.Receiver
|
|||||||
int Id,
|
int Id,
|
||||||
string EmailAddress,
|
string EmailAddress,
|
||||||
string Signature,
|
string Signature,
|
||||||
DateTime AddedWhen,
|
DateTime AddedWhen
|
||||||
string? TotpSecretkey = null,
|
) : BaseDTO<int>(Id), IUnique<int>
|
||||||
DateTime? TotpExpiration = null
|
|
||||||
) : BaseDTO<int>(Id)
|
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public IEnumerable<EnvelopeReceiverBasicDto>? EnvelopeReceivers { get; init; }
|
public IEnumerable<EnvelopeReceiverBasicDto>? EnvelopeReceivers { get; init; }
|
||||||
|
|
||||||
public string? LastUsedName => EnvelopeReceivers?.LastOrDefault()?.Name;
|
public string? LastUsedName => EnvelopeReceivers?.LastOrDefault()?.Name;
|
||||||
|
|
||||||
|
public string? TotpSecretkey { get; set; } = null;
|
||||||
|
|
||||||
|
[TemplatePlaceholder("[TFA_QR_EXPIRATION]")]
|
||||||
|
public DateTime? TotpExpiration { get; set; } = null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,6 @@
|
|||||||
<PackageReference Include="DigitalData.Core.DTO" Version="2.0.0" />
|
<PackageReference Include="DigitalData.Core.DTO" Version="2.0.0" />
|
||||||
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="2.0.0" />
|
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.18" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.18" />
|
||||||
<PackageReference Include="Otp.NET" Version="1.4.0" />
|
|
||||||
<PackageReference Include="QRCoder" Version="1.6.0" />
|
<PackageReference Include="QRCoder" Version="1.6.0" />
|
||||||
<PackageReference Include="QRCoder-ImageSharp" Version="0.10.0" />
|
<PackageReference Include="QRCoder-ImageSharp" Version="0.10.0" />
|
||||||
<PackageReference Include="UserManager.Application" Version="2.0.0" />
|
<PackageReference Include="UserManager.Application" Version="2.0.0" />
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace EnvelopeGenerator.Application.MappingProfiles
|
|||||||
CreateMap<EnvelopeHistoryCreateDto, EnvelopeHistory>();
|
CreateMap<EnvelopeHistoryCreateDto, EnvelopeHistory>();
|
||||||
CreateMap<EnvelopeReceiverDto, EnvelopeReceiver>();
|
CreateMap<EnvelopeReceiverDto, EnvelopeReceiver>();
|
||||||
CreateMap<EnvelopeTypeDto, EnvelopeType>();
|
CreateMap<EnvelopeTypeDto, EnvelopeType>();
|
||||||
CreateMap<ReceiverReadDto, Receiver>();
|
CreateMap<ReceiverReadDto, Receiver>().ForMember(rcv => rcv.EnvelopeReceivers, rcvReadDto => rcvReadDto.Ignore());
|
||||||
CreateMap<ReceiverCreateDto, Receiver>();
|
CreateMap<ReceiverCreateDto, Receiver>();
|
||||||
CreateMap<ReceiverUpdateDto, Receiver>();
|
CreateMap<ReceiverUpdateDto, Receiver>();
|
||||||
CreateMap<UserReceiverDto, UserReceiver>();
|
CreateMap<UserReceiverDto, UserReceiver>();
|
||||||
|
|||||||
@@ -159,41 +159,56 @@
|
|||||||
<data name="HomePageDescription" xml:space="preserve">
|
<data name="HomePageDescription" xml:space="preserve">
|
||||||
<value>Das digitale Unterschriftenportal ist eine Plattform, die entwickelt wurde, um Ihre Dokumente sicher zu unterschreiben und zu verwalten. Mit seiner benutzerfreundlichen Oberfläche können Sie Ihre Dokumente schnell hochladen, die Unterschriftsprozesse verfolgen und Ihre digitalen Unterschriftenanwendungen einfach durchführen. Dieses Portal beschleunigt Ihren Arbeitsablauf mit rechtlich gültigen Unterschriften und erhöht gleichzeitig die Sicherheit Ihrer Dokumente.</value>
|
<value>Das digitale Unterschriftenportal ist eine Plattform, die entwickelt wurde, um Ihre Dokumente sicher zu unterschreiben und zu verwalten. Mit seiner benutzerfreundlichen Oberfläche können Sie Ihre Dokumente schnell hochladen, die Unterschriftsprozesse verfolgen und Ihre digitalen Unterschriftenanwendungen einfach durchführen. Dieses Portal beschleunigt Ihren Arbeitsablauf mit rechtlich gültigen Unterschriften und erhöht gleichzeitig die Sicherheit Ihrer Dokumente.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LocakedOpen" xml:space="preserve">
|
|
||||||
<value>Öffnen</value>
|
|
||||||
</data>
|
|
||||||
<data name="LocationWarning" xml:space="preserve">
|
<data name="LocationWarning" xml:space="preserve">
|
||||||
<value>Bitte überprüfen Sie die Standortinformationen. Wenn sie falsch sind, korrigieren Sie diese bitte.</value>
|
<value>Bitte überprüfen Sie die Standortinformationen. Wenn sie falsch sind, korrigieren Sie diese bitte.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedAccessCode" xml:space="preserve">
|
<data name="LockedBodyAccess" xml:space="preserve">
|
||||||
<value>Zugriffscode</value>
|
|
||||||
</data>
|
|
||||||
<data name="LockedBody" xml:space="preserve">
|
|
||||||
<value>Wir haben Ihnen gerade den Zugriffscode an die hinterlegte Email Adresse gesendet. Dies kann evtl. einige Minuten dauern.</value>
|
<value>Wir haben Ihnen gerade den Zugriffscode an die hinterlegte Email Adresse gesendet. Dies kann evtl. einige Minuten dauern.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedFooterBody" xml:space="preserve">
|
<data name="LockedBodyAuthenticator" xml:space="preserve">
|
||||||
<value>Bitte überprüfen Sie Ihr Email Postfach inklusive Spam-Ordner. Sie können auch den Absender bitten, Ihnen den Code auf anderem Wege zukommen zu lassen.</value>
|
<value>Ihr QR-Code ist bis {0} gültig.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedFooterTitle" xml:space="preserve">
|
<data name="LockedBodyAuthenticatorNew" xml:space="preserve">
|
||||||
<value>Sie haben keinen Zugriffscode erhalten?</value>
|
<value>Wir haben den QR-Code an Ihre E-Mail-Adresse gesendet. Ihr QR-Code ist bis {0} gültig. Sie können ihn für alle Umschläge verwenden, die Sie an diese E-Mail-Adresse erhalten.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedSmsAccessCode" xml:space="preserve">
|
<data name="LockedBodySms" xml:space="preserve">
|
||||||
<value>SMS-Code</value>
|
|
||||||
</data>
|
|
||||||
<data name="LockedSmsTfaBody" xml:space="preserve">
|
|
||||||
<value>Wir haben soeben den Zugangscode als SMS an die von Ihnen angegebene Telefonnummer gesendet.</value>
|
<value>Wir haben soeben den Zugangscode als SMS an die von Ihnen angegebene Telefonnummer gesendet.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedSmsTfaFooterBody" xml:space="preserve">
|
<data name="LockedCodeLabelAccess" xml:space="preserve">
|
||||||
|
<value>Zugriffscode</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedCodeLabelAuthenticator" xml:space="preserve">
|
||||||
|
<value>TOTP</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedCodeLabelSms" xml:space="preserve">
|
||||||
|
<value>SMS-Code</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedFooterBodyAccess" xml:space="preserve">
|
||||||
|
<value>Bitte überprüfen Sie Ihr Email Postfach inklusive Spam-Ordner. Sie können auch den Absender bitten, Ihnen den Code auf anderem Wege zukommen zu lassen.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedFooterBodyAuthenticator" xml:space="preserve">
|
||||||
|
<value>Der neue QR-Code wird nur einmal für einen bestimmten Zeitraum gesendet und nach dem Scannen in Ihrer Authenticator-App gespeichert. Er kann für alle Umschläge verwendet werden, die an dieselbe E-Mail-Adresse gesendet werden, bis er abläuft. Wenn Sie die QR-Code-Mail nicht erhalten oder sie sowohl aus der Mail als auch aus authenticator löschen, kontaktieren Sie bitte den Absender.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedFooterBodySms" xml:space="preserve">
|
||||||
<value>Sie können den Absender bitten, Ihre Rufnummer zu überprüfen. Die Telefonnummer muss mit der Ortsvorwahl eingegeben werden. Andernfalls können Sie beantragen, den Zwei-Faktor-Schutz zu entfernen.</value>
|
<value>Sie können den Absender bitten, Ihre Rufnummer zu überprüfen. Die Telefonnummer muss mit der Ortsvorwahl eingegeben werden. Andernfalls können Sie beantragen, den Zwei-Faktor-Schutz zu entfernen.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedSmsTfaFooterTitle" xml:space="preserve">
|
<data name="LockedFooterTitleAccess" xml:space="preserve">
|
||||||
|
<value>Sie haben keinen Zugriffscode erhalten?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedFooterTitleAuthenticator" xml:space="preserve">
|
||||||
|
<value>Sie haben keinen QR-Code erhalten?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedFooterTitleSms" xml:space="preserve">
|
||||||
<value>Sie haben keine SMS erhalten?</value>
|
<value>Sie haben keine SMS erhalten?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedSmsTfaTitle" xml:space="preserve">
|
<data name="LockedTitleAccess" xml:space="preserve">
|
||||||
|
<value>Dokument erfordert einen Zugriffscode</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedTitleAuthenticator" xml:space="preserve">
|
||||||
<value>2-Faktor-Authentifizierung</value>
|
<value>2-Faktor-Authentifizierung</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedTitle" xml:space="preserve">
|
<data name="LockedTitleSms" xml:space="preserve">
|
||||||
<value>Dokument erfordert einen Zugriffscode</value>
|
<value>2-Faktor-Authentifizierung</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Privacy" xml:space="preserve">
|
<data name="Privacy" xml:space="preserve">
|
||||||
<value>Datenschutz</value>
|
<value>Datenschutz</value>
|
||||||
|
|||||||
@@ -159,41 +159,56 @@
|
|||||||
<data name="HomePageDescription" xml:space="preserve">
|
<data name="HomePageDescription" xml:space="preserve">
|
||||||
<value>The Digital Signature Portal is a platform developed for securely signing and managing your documents. With its user-friendly interface, you can quickly upload your documents, track the signing processes, and easily carry out your digital signature applications. This portal accelerates your workflow with legally valid signatures while enhancing the security of your documents.</value>
|
<value>The Digital Signature Portal is a platform developed for securely signing and managing your documents. With its user-friendly interface, you can quickly upload your documents, track the signing processes, and easily carry out your digital signature applications. This portal accelerates your workflow with legally valid signatures while enhancing the security of your documents.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LocakedOpen" xml:space="preserve">
|
|
||||||
<value>Open</value>
|
|
||||||
</data>
|
|
||||||
<data name="LocationWarning" xml:space="preserve">
|
<data name="LocationWarning" xml:space="preserve">
|
||||||
<value>Please review the location information. If it is incorrect, kindly make the necessary corrections.</value>
|
<value>Please review the location information. If it is incorrect, kindly make the necessary corrections.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedAccessCode" xml:space="preserve">
|
<data name="LockedBodyAccess" xml:space="preserve">
|
||||||
<value>Access Code</value>
|
|
||||||
</data>
|
|
||||||
<data name="LockedBody" xml:space="preserve">
|
|
||||||
<value>We have just sent you the access code to the email address you provided. This may take a few minutes.</value>
|
<value>We have just sent you the access code to the email address you provided. This may take a few minutes.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedFooterBody" xml:space="preserve">
|
<data name="LockedBodyAuthenticator" xml:space="preserve">
|
||||||
<value>Please check your email inbox including your spam folder. Furthermore, you can also ask the sender to send the code by other means.</value>
|
<value>Your QR code is valid until {0}.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedFooterTitle" xml:space="preserve">
|
<data name="LockedBodyAuthenticatorNew" xml:space="preserve">
|
||||||
<value>You have not received an access code?</value>
|
<value>We have sent the QR code to your e-mail address. Your QR code is valid until {0}. You can use it for all envelopes received at this email address.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedSmsAccessCode" xml:space="preserve">
|
<data name="LockedBodySms" xml:space="preserve">
|
||||||
<value>SMS Code</value>
|
|
||||||
</data>
|
|
||||||
<data name="LockedSmsTfaBody" xml:space="preserve">
|
|
||||||
<value>We have just sent the access code as an SMS to the phone number you provided.</value>
|
<value>We have just sent the access code as an SMS to the phone number you provided.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedSmsTfaFooterBody" xml:space="preserve">
|
<data name="LockedCodeLabelAccess" xml:space="preserve">
|
||||||
|
<value>Access Code</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedCodeLabelAuthenticator" xml:space="preserve">
|
||||||
|
<value>TOTP</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedCodeLabelSms" xml:space="preserve">
|
||||||
|
<value>SMS Code</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedFooterBodyAccess" xml:space="preserve">
|
||||||
|
<value>Please check your email inbox including your spam folder. Furthermore, you can also ask the sender to send the code by other means.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedFooterBodyAuthenticator" xml:space="preserve">
|
||||||
|
<value>The new QR code is sent only once for a given period and is saved in your authenticator app once scanned. It can be used for all envelopes received at the same email address until it expires. If you do not receive the QR code mail or delete it both from the mail and from authenticator, please contact the sender.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedFooterBodySms" xml:space="preserve">
|
||||||
<value>You can ask the sender to check your phone number. The phone number must be entered with the area code. Otherwise you can request to remove the two-factor protection.</value>
|
<value>You can ask the sender to check your phone number. The phone number must be entered with the area code. Otherwise you can request to remove the two-factor protection.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedSmsTfaFooterTitle" xml:space="preserve">
|
<data name="LockedFooterTitleAccess" xml:space="preserve">
|
||||||
|
<value>You have not received an access code?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedFooterTitleAuthenticator" xml:space="preserve">
|
||||||
|
<value>You have not received a QR code?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedFooterTitleSms" xml:space="preserve">
|
||||||
<value>You have not received an SMS?</value>
|
<value>You have not received an SMS?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedSmsTfaTitle" xml:space="preserve">
|
<data name="LockedTitleAccess" xml:space="preserve">
|
||||||
|
<value>Document requires an access code</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockedTitleAuthenticator" xml:space="preserve">
|
||||||
<value>2-Factor Authentication</value>
|
<value>2-Factor Authentication</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockedTitle" xml:space="preserve">
|
<data name="LockedTitleSms" xml:space="preserve">
|
||||||
<value>Document requires an access code</value>
|
<value>2-Factor Authentication</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Privacy" xml:space="preserve">
|
<data name="Privacy" xml:space="preserve">
|
||||||
<value>Privacy</value>
|
<value>Privacy</value>
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ using static EnvelopeGenerator.Common.Constants;
|
|||||||
using EnvelopeGenerator.Extensions;
|
using EnvelopeGenerator.Extensions;
|
||||||
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
|
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
|
||||||
using EnvelopeGenerator.Application.Configurations;
|
using EnvelopeGenerator.Application.Configurations;
|
||||||
|
using EnvelopeGenerator.Application.Extensions;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Services
|
namespace EnvelopeGenerator.Application.Services
|
||||||
{
|
{
|
||||||
@@ -22,17 +24,19 @@ namespace EnvelopeGenerator.Application.Services
|
|||||||
private readonly DispatcherConfig _dConfig;
|
private readonly DispatcherConfig _dConfig;
|
||||||
private readonly IConfigService _configService;
|
private readonly IConfigService _configService;
|
||||||
private readonly Dictionary<string, string> _placeholders;
|
private readonly Dictionary<string, string> _placeholders;
|
||||||
|
private readonly ICodeGenerator _codeGenerator;
|
||||||
|
|
||||||
public EnvelopeMailService(IEmailOutRepository repository, IMapper mapper, IEmailTemplateService tempService, IEnvelopeReceiverService envelopeReceiverService, IOptions<DispatcherConfig> dispatcherConfigOptions, IConfigService configService, IOptions<MailConfig> mailConfig) : base(repository, mapper)
|
public EnvelopeMailService(IEmailOutRepository repository, IMapper mapper, IEmailTemplateService tempService, IEnvelopeReceiverService envelopeReceiverService, IOptions<DispatcherConfig> dispatcherConfigOptions, IConfigService configService, IOptions<MailConfig> mailConfig, ICodeGenerator codeGenerator) : base(repository, mapper)
|
||||||
{
|
{
|
||||||
_tempService = tempService;
|
_tempService = tempService;
|
||||||
_envRcvService = envelopeReceiverService;
|
_envRcvService = envelopeReceiverService;
|
||||||
_dConfig = dispatcherConfigOptions.Value;
|
_dConfig = dispatcherConfigOptions.Value;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_placeholders = mailConfig.Value.Placeholders;
|
_placeholders = mailConfig.Value.Placeholders;
|
||||||
|
_codeGenerator = codeGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Dictionary<string, string>> CreatePlaceholders(string? accessCode = null, EnvelopeReceiverDto? envelopeReceiverDto = null, EnvelopeReceiverReadOnlyDto? readOnlyDto = null)
|
private async Task<Dictionary<string, string>> CreatePlaceholders(string? accessCode = null, EnvelopeReceiverDto? envelopeReceiverDto = null)
|
||||||
{
|
{
|
||||||
if (accessCode is not null)
|
if (accessCode is not null)
|
||||||
_placeholders["[DOCUMENT_ACCESS_CODE]"] = accessCode;
|
_placeholders["[DOCUMENT_ACCESS_CODE]"] = accessCode;
|
||||||
@@ -64,9 +68,7 @@ namespace EnvelopeGenerator.Application.Services
|
|||||||
return _placeholders;
|
return _placeholders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<DataResult<int>> SendAccessCodeAsync(EnvelopeReceiverDto dto) => await SendAsync(dto: dto, tempType: Constants.EmailTemplateType.DocumentAccessCodeReceived);
|
public async Task<DataResult<int>> SendAsync(EnvelopeReceiverDto dto, EmailTemplateType tempType, Dictionary<string, object>? optionalPlaceholders = null)
|
||||||
|
|
||||||
public async Task<DataResult<int>> SendAsync(EnvelopeReceiverDto dto, Constants.EmailTemplateType tempType)
|
|
||||||
{
|
{
|
||||||
var tempSerResult = await _tempService.ReadByNameAsync(tempType);
|
var tempSerResult = await _tempService.ReadByNameAsync(tempType);
|
||||||
if (tempSerResult.IsFailed)
|
if (tempSerResult.IsFailed)
|
||||||
@@ -104,14 +106,19 @@ namespace EnvelopeGenerator.Application.Services
|
|||||||
|
|
||||||
var placeholders = await CreatePlaceholders(accessCode: accessCode, envelopeReceiverDto: dto);
|
var placeholders = await CreatePlaceholders(accessCode: accessCode, envelopeReceiverDto: dto);
|
||||||
|
|
||||||
|
// Add optional place holders.
|
||||||
|
if (optionalPlaceholders is not null)
|
||||||
|
foreach (var oph in optionalPlaceholders)
|
||||||
|
placeholders[oph.Key] = oph.Value.ToString() ?? "NULL";
|
||||||
|
|
||||||
//TODO: remove the requirement to add the models using reflections
|
//TODO: remove the requirement to add the models using reflections
|
||||||
return await CreateWithTemplateAsync(createDto: mail,placeholders: placeholders,
|
return await CreateWithTemplateAsync(createDto: mail,placeholders: placeholders,
|
||||||
dto, dto.Envelope.User!, dto.Envelope);
|
dto, dto.Envelope.User!, dto.Envelope);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<DataResult<int>> SendAsync(EnvelopeReceiverReadOnlyDto dto)
|
public async Task<DataResult<int>> SendAsync(EnvelopeReceiverReadOnlyDto dto, Dictionary<string, object>? optionalPlaceholders = null)
|
||||||
{
|
{
|
||||||
var tempSerResult = await _tempService.ReadByNameAsync(Constants.EmailTemplateType.DocumentShared);
|
var tempSerResult = await _tempService.ReadByNameAsync(EmailTemplateType.DocumentShared);
|
||||||
if (tempSerResult.IsFailed)
|
if (tempSerResult.IsFailed)
|
||||||
return tempSerResult.ToFail<int>().Notice(LogLevel.Error, Flag.DataIntegrityIssue, $"The email cannot send because '{Constants.EmailTemplateType.DocumentShared}' template cannot found.");
|
return tempSerResult.ToFail<int>().Notice(LogLevel.Error, Flag.DataIntegrityIssue, $"The email cannot send because '{Constants.EmailTemplateType.DocumentShared}' template cannot found.");
|
||||||
var temp = tempSerResult.Data;
|
var temp = tempSerResult.Data;
|
||||||
@@ -140,7 +147,32 @@ namespace EnvelopeGenerator.Application.Services
|
|||||||
|
|
||||||
var placeholders = await CreatePlaceholders(readOnlyDto: dto);
|
var placeholders = await CreatePlaceholders(readOnlyDto: dto);
|
||||||
|
|
||||||
|
// Add optional place holders.
|
||||||
|
if (optionalPlaceholders is not null)
|
||||||
|
foreach (var oph in optionalPlaceholders)
|
||||||
|
placeholders[oph.Key] = oph.Value.ToString() ?? "NULL";
|
||||||
|
|
||||||
return await CreateWithTemplateAsync(createDto: mail, placeholders: placeholders, dto.Envelope);
|
return await CreateWithTemplateAsync(createDto: mail, placeholders: placeholders, dto.Envelope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<DataResult<int>> SendAccessCodeAsync(EnvelopeReceiverDto dto) => await SendAsync(dto: dto, tempType: EmailTemplateType.DocumentAccessCodeReceived);
|
||||||
|
|
||||||
|
public Task<DataResult<int>> SendTFAQrCodeAsync(EnvelopeReceiverDto dto)
|
||||||
|
{
|
||||||
|
// Check if receiver or secret key is null
|
||||||
|
if (dto.Receiver is null)
|
||||||
|
throw new ArgumentNullException(nameof(dto), $"TFA Qr Code cannot sent. Receiver information is missing. Envelope receiver dto is {JsonConvert.SerializeObject(dto)}");
|
||||||
|
if (dto.Receiver.TotpSecretkey is null)
|
||||||
|
throw new ArgumentNullException(nameof(dto), $"TFA Qr Code cannot sent. Receiver.TotpSecretKey is null. Envelope receiver dto is {JsonConvert.SerializeObject(dto)}");
|
||||||
|
if (dto.Receiver.TotpExpiration is null)
|
||||||
|
throw new ArgumentNullException(nameof(dto), $"TFA Qr Code cannot sent. Receiver.TotpExpiration is null. Envelope receiver dto is {JsonConvert.SerializeObject(dto)}");
|
||||||
|
|
||||||
|
var totp_qr_64 = _codeGenerator.GenerateTotpQrCode(userEmail: dto.Receiver.EmailAddress, secretKey: dto.Receiver.TotpSecretkey).ToBase64String();
|
||||||
|
return SendAsync(dto, EmailTemplateType.TotpSecret, new()
|
||||||
|
{
|
||||||
|
{"[TFA_QR_CODE]", totp_qr_64 },
|
||||||
|
{"[TFA_EXPIRATION]", dto.Receiver.TotpExpiration }
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,8 @@ using EnvelopeGenerator.Domain.Entities;
|
|||||||
using EnvelopeGenerator.Infrastructure.Contracts;
|
using EnvelopeGenerator.Infrastructure.Contracts;
|
||||||
using EnvelopeGenerator.Application.DTOs.Receiver;
|
using EnvelopeGenerator.Application.DTOs.Receiver;
|
||||||
using DigitalData.Core.DTO;
|
using DigitalData.Core.DTO;
|
||||||
|
using DigitalData.Core.Abstractions;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Services
|
namespace EnvelopeGenerator.Application.Services
|
||||||
{
|
{
|
||||||
@@ -34,5 +36,17 @@ namespace EnvelopeGenerator.Application.Services
|
|||||||
|
|
||||||
return await _repository.DeleteAsync(rcv) ? Result.Success() : Result.Fail();
|
return await _repository.DeleteAsync(rcv) ? Result.Success() : Result.Fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual async Task<Result> UpdateAsync<TUpdateDto>(TUpdateDto updateDto) where TUpdateDto : IUnique<int>
|
||||||
|
{
|
||||||
|
var val = await _repository.ReadByIdAsync(updateDto.Id);
|
||||||
|
if (val == null)
|
||||||
|
{
|
||||||
|
return Result.Fail().Notice(LogLevel.Warning, Flag.NotFound, $"{updateDto.Id} is not found in update process of {GetType()} entity.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var entity = _mapper.Map(updateDto, val);
|
||||||
|
return (await _repository.UpdateAsync(entity)) ? Result.Success() : Result.Fail();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,6 +99,7 @@
|
|||||||
DocumentCompleted
|
DocumentCompleted
|
||||||
DocumentAccessCodeReceived
|
DocumentAccessCodeReceived
|
||||||
DocumentShared
|
DocumentShared
|
||||||
|
TotpSecret
|
||||||
End Enum
|
End Enum
|
||||||
|
|
||||||
Public Enum EncodeType
|
Public Enum EncodeType
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<PackageReference Include="HtmlSanitizer" Version="8.0.865" />
|
<PackageReference Include="HtmlSanitizer" Version="8.0.865" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.19" />
|
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.19" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Otp.NET" Version="1.4.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
14
EnvelopeGenerator.Extensions/StringExtension.cs
Normal file
14
EnvelopeGenerator.Extensions/StringExtension.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using OtpNet;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Extensions
|
||||||
|
{
|
||||||
|
public static class StringExtension
|
||||||
|
{
|
||||||
|
public static bool IsValidTotp(this string totp, string secret)
|
||||||
|
{
|
||||||
|
var secret_bytes = Base32Encoding.ToBytes(secret);
|
||||||
|
var secret_totp = new Totp(secret_bytes);
|
||||||
|
return secret_totp.VerifyTotp(totp, out _, VerificationWindow.RfcSpecifiedNetworkDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ using Ganss.Xss;
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using EnvelopeGenerator.Application.DTOs;
|
using EnvelopeGenerator.Application.DTOs;
|
||||||
using DigitalData.Core.Client;
|
using DigitalData.Core.Client;
|
||||||
using System.Text.Json;
|
using EnvelopeGenerator.Application.Extensions;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Web.Controllers
|
namespace EnvelopeGenerator.Web.Controllers
|
||||||
{
|
{
|
||||||
@@ -37,8 +37,10 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
private readonly IEnvelopeReceiverReadOnlyService _readOnlyService;
|
private readonly IEnvelopeReceiverReadOnlyService _readOnlyService;
|
||||||
private readonly IMessagingService _msgService;
|
private readonly IMessagingService _msgService;
|
||||||
private readonly IEnvelopeReceiverCache _erCache;
|
private readonly IEnvelopeReceiverCache _erCache;
|
||||||
|
private readonly ICodeGenerator _codeGenerator;
|
||||||
|
private readonly IReceiverService _rcvService;
|
||||||
|
|
||||||
public HomeController(EnvelopeOldService envelopeOldService, ILogger<HomeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer<Resource> localizer, IConfiguration configuration, HtmlSanitizer sanitizer, Cultures cultures, IEnvelopeMailService envelopeMailService, IEnvelopeReceiverReadOnlyService readOnlyService, IMessagingService messagingService, IEnvelopeReceiverCache envelopeReceiverCache)
|
public HomeController(EnvelopeOldService envelopeOldService, ILogger<HomeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer<Resource> localizer, IConfiguration configuration, HtmlSanitizer sanitizer, Cultures cultures, IEnvelopeMailService envelopeMailService, IEnvelopeReceiverReadOnlyService readOnlyService, IMessagingService messagingService, IEnvelopeReceiverCache envelopeReceiverCache, ICodeGenerator codeGenerator, IReceiverService receiverService)
|
||||||
{
|
{
|
||||||
this.envelopeOldService = envelopeOldService;
|
this.envelopeOldService = envelopeOldService;
|
||||||
_envRcvService = envelopeReceiverService;
|
_envRcvService = envelopeReceiverService;
|
||||||
@@ -52,6 +54,8 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
_readOnlyService = readOnlyService;
|
_readOnlyService = readOnlyService;
|
||||||
_msgService = messagingService;
|
_msgService = messagingService;
|
||||||
_erCache = envelopeReceiverCache;
|
_erCache = envelopeReceiverCache;
|
||||||
|
_codeGenerator = codeGenerator;
|
||||||
|
_rcvService = receiverService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("/")]
|
[HttpGet("/")]
|
||||||
@@ -188,9 +192,9 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
{
|
{
|
||||||
var res = await _msgService.SendSmsCodeAsync(er_secret.PhoneNumber!, envelopeReceiverId: envelopeReceiverId);
|
var res = await _msgService.SendSmsCodeAsync(er_secret.PhoneNumber!, envelopeReceiverId: envelopeReceiverId);
|
||||||
if (res.Ok)
|
if (res.Ok)
|
||||||
return View("EnvelopeLocked").WithData("AccessCodeName", "smsCode").WithData("Expiration", res.Expiration);
|
return View("EnvelopeLocked").WithData("CodeType", "smsCode").WithData("SmsExpiration", res.Expiration);
|
||||||
else if (!res.Allowed)
|
else if (!res.Allowed)
|
||||||
return View("EnvelopeLocked").WithData("AccessCodeName", "smsCode").WithData("Expiration", res.AllowedAt);
|
return View("EnvelopeLocked").WithData("CodeType", "smsCode").WithData("SmsExpiration", res.AllowedAt);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var res_json = JsonConvert.SerializeObject(res);
|
var res_json = JsonConvert.SerializeObject(res);
|
||||||
@@ -200,7 +204,7 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return View("EnvelopeLocked").WithData("AccessCodeName", "authenticatorCode");
|
return View("EnvelopeLocked").WithData("CodeType", "authenticatorCode").WithData("QRCodeExpiration", er_secret.Receiver?.TotpExpiration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,18 +220,29 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
if (er_secret.AccessCode != auth.AccessCode)
|
if (er_secret.AccessCode != auth.AccessCode)
|
||||||
{
|
{
|
||||||
//Constants.EnvelopeStatus.AccessCodeIncorrect
|
//Constants.EnvelopeStatus.AccessCodeIncorrect
|
||||||
await _historyService.RecordAsync(er_secret.EnvelopeId, er_secret.Receiver!.EmailAddress, Constants.EnvelopeStatus.AccessCodeIncorrect);
|
await _historyService.RecordAsync(er_secret.EnvelopeId, er_secret.Receiver!.EmailAddress, EnvelopeStatus.AccessCodeIncorrect);
|
||||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||||
return View("EnvelopeLocked")
|
return View("EnvelopeLocked")
|
||||||
.WithData("ErrorMessage", _localizer[WebKey.WrongAccessCode].Value);
|
.WithData("ErrorMessage", _localizer[WebKey.WrongAccessCode].Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _historyService.RecordAsync(er_secret.EnvelopeId, er_secret.Receiver!.EmailAddress, Constants.EnvelopeStatus.AccessCodeCorrect);
|
await _historyService.RecordAsync(er_secret.EnvelopeId, er_secret.Receiver!.EmailAddress, EnvelopeStatus.AccessCodeCorrect);
|
||||||
|
|
||||||
//check if the user has phone is added
|
//check if the user has phone is added
|
||||||
if (er_secret.TFAEnabled)
|
if (er_secret.TFAEnabled)
|
||||||
|
{
|
||||||
|
var rcv = er_secret.Receiver;
|
||||||
|
if (rcv.IsTotpSecretInvalid())
|
||||||
|
{
|
||||||
|
rcv.TotpSecretkey = _codeGenerator.GenerateTotpSecretKey();
|
||||||
|
rcv.TotpExpiration = DateTime.Now.AddMonths(1);
|
||||||
|
await _rcvService.UpdateAsync(rcv);
|
||||||
|
await _mailService.SendTFAQrCodeAsync(er_secret);
|
||||||
|
}
|
||||||
return await TFAView(auth.UserSelectSMS);
|
return await TFAView(auth.UserSelectSMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
else if (auth.HasSmsCode)
|
else if (auth.HasSmsCode)
|
||||||
{
|
{
|
||||||
var smsCode = await _erCache.GetSmsCodeAsync(envelopeReceiverId);
|
var smsCode = await _erCache.GetSmsCodeAsync(envelopeReceiverId);
|
||||||
@@ -241,6 +256,15 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
return await TFAView(viaSms: true);
|
return await TFAView(viaSms: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (auth.HasAuthenticatorCode)
|
||||||
|
{
|
||||||
|
if (!auth.AuthenticatorCode!.IsValidTotp(er_secret.Receiver!.TotpSecretkey!))
|
||||||
|
{
|
||||||
|
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||||
|
ViewData["ErrorMessage"] = _localizer[WebKey.WrongAccessCode].Value;
|
||||||
|
return await TFAView(viaSms: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||||
|
|||||||
@@ -6,12 +6,14 @@
|
|||||||
var logo = _logoOpt.Value;
|
var logo = _logoOpt.Value;
|
||||||
ViewData["Title"] = _localizer[WebKey.DocProtected];
|
ViewData["Title"] = _localizer[WebKey.DocProtected];
|
||||||
var userCulture = ViewData["UserCulture"] as Culture;
|
var userCulture = ViewData["UserCulture"] as Culture;
|
||||||
string accessCodeName = ViewData["AccessCodeName"] is string _accessCodeName ? _accessCodeName : "accessCode";
|
string codeType = ViewData["CodeType"] is string _codeType ? _codeType : "accessCode";
|
||||||
string codePropName = char.ToUpper(accessCodeName[0]) + accessCodeName.Substring(1);
|
string codePropName = char.ToUpper(codeType[0]) + codeType.Substring(1);
|
||||||
bool viaSms = accessCodeName == "smsCode";
|
string codeKeyName = codePropName.Replace("Code", "");
|
||||||
bool viaAuthenticator = accessCodeName == "authenticatorCode";
|
bool viaSms = codeType == "smsCode";
|
||||||
|
bool viaAuthenticator = codeType == "authenticatorCode";
|
||||||
bool viaTFA = viaSms || viaAuthenticator;
|
bool viaTFA = viaSms || viaAuthenticator;
|
||||||
DateTime? expiration = ViewData["Expiration"] is DateTime _expiration ? _expiration : null;
|
DateTime? smsExpiration = ViewData["SmsExpiration"] is DateTime _smsExpiration ? _smsExpiration : null;
|
||||||
|
DateTime? qrCodeExpiration = ViewData["QRCodeExpiration"] is DateTime _qrCodeExpiration ? _qrCodeExpiration : null;
|
||||||
bool tfaEnabled = ViewData["TFAEnabled"] is bool _tfaEnabled && _tfaEnabled;
|
bool tfaEnabled = ViewData["TFAEnabled"] is bool _tfaEnabled && _tfaEnabled;
|
||||||
bool hasPhoneNumber = ViewData["HasPhoneNumber"] is bool _hasPhoneNumber && _hasPhoneNumber;
|
bool hasPhoneNumber = ViewData["HasPhoneNumber"] is bool _hasPhoneNumber && _hasPhoneNumber;
|
||||||
}
|
}
|
||||||
@@ -21,23 +23,23 @@
|
|||||||
<h3 class="text">@_localizer[WebKey.WelcomeToTheESignPortal]</h3>
|
<h3 class="text">@_localizer[WebKey.WelcomeToTheESignPortal]</h3>
|
||||||
<img class="@logo.LockedPageClass" src="@logo.Src" />
|
<img class="@logo.LockedPageClass" src="@logo.Src" />
|
||||||
</div>
|
</div>
|
||||||
<div class="icon locked @(viaSms ? "sms-tfa" : "") mt-4 mb-1">
|
<div class="icon locked @(viaTFA ? "tfa" : "") mt-4 mb-1">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="72" height="72" fill="currentColor" class="bi bi-shield-lock" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="72" height="72" fill="currentColor" class="bi bi-shield-lock" viewBox="0 0 16 16">
|
||||||
<path d="M5.338 1.59a61 61 0 0 0-2.837.856.48.48 0 0 0-.328.39c-.554 4.157.726 7.19 2.253 9.188a10.7 10.7 0 0 0 2.287 2.233c.346.244.652.42.893.533q.18.085.293.118a1 1 0 0 0 .101.025 1 1 0 0 0 .1-.025q.114-.034.294-.118c.24-.113.547-.29.893-.533a10.7 10.7 0 0 0 2.287-2.233c1.527-1.997 2.807-5.031 2.253-9.188a.48.48 0 0 0-.328-.39c-.651-.213-1.75-.56-2.837-.855C9.552 1.29 8.531 1.067 8 1.067c-.53 0-1.552.223-2.662.524zM5.072.56C6.157.265 7.31 0 8 0s1.843.265 2.928.56c1.11.3 2.229.655 2.887.87a1.54 1.54 0 0 1 1.044 1.262c.596 4.477-.787 7.795-2.465 9.99a11.8 11.8 0 0 1-2.517 2.453 7 7 0 0 1-1.048.625c-.28.132-.581.24-.829.24s-.548-.108-.829-.24a7 7 0 0 1-1.048-.625 11.8 11.8 0 0 1-2.517-2.453C1.928 10.487.545 7.169 1.141 2.692A1.54 1.54 0 0 1 2.185 1.43 63 63 0 0 1 5.072.56" />
|
<path d="M5.338 1.59a61 61 0 0 0-2.837.856.48.48 0 0 0-.328.39c-.554 4.157.726 7.19 2.253 9.188a10.7 10.7 0 0 0 2.287 2.233c.346.244.652.42.893.533q.18.085.293.118a1 1 0 0 0 .101.025 1 1 0 0 0 .1-.025q.114-.034.294-.118c.24-.113.547-.29.893-.533a10.7 10.7 0 0 0 2.287-2.233c1.527-1.997 2.807-5.031 2.253-9.188a.48.48 0 0 0-.328-.39c-.651-.213-1.75-.56-2.837-.855C9.552 1.29 8.531 1.067 8 1.067c-.53 0-1.552.223-2.662.524zM5.072.56C6.157.265 7.31 0 8 0s1.843.265 2.928.56c1.11.3 2.229.655 2.887.87a1.54 1.54 0 0 1 1.044 1.262c.596 4.477-.787 7.795-2.465 9.99a11.8 11.8 0 0 1-2.517 2.453 7 7 0 0 1-1.048.625c-.28.132-.581.24-.829.24s-.548-.108-.829-.24a7 7 0 0 1-1.048-.625 11.8 11.8 0 0 1-2.517-2.453C1.928 10.487.545 7.169 1.141 2.692A1.54 1.54 0 0 1 2.185 1.43 63 63 0 0 1 5.072.56" />
|
||||||
<path d="M9.5 6.5a1.5 1.5 0 0 1-1 1.415l.385 1.99a.5.5 0 0 1-.491.595h-.788a.5.5 0 0 1-.49-.595l.384-1.99a1.5 1.5 0 1 1 2-1.415" />
|
<path d="M9.5 6.5a1.5 1.5 0 0 1-1 1.415l.385 1.99a.5.5 0 0 1-.491.595h-.788a.5.5 0 0 1-.49-.595l.384-1.99a1.5 1.5 0 1 1 2-1.415" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h1>@_localizer[viaSms ? WebKey.LockedSmsTfaTitle : WebKey.LockedTitle]</h1>
|
<h1>@_localizer[WebKey.Formats.LockedTitle.Format(codeKeyName)]</h1>
|
||||||
</header>
|
</header>
|
||||||
<section class="text-center">
|
<section class="text-center">
|
||||||
<p>@_localizer[viaSms ? WebKey.LockedSmsTfaBody : WebKey.LockedBody]</p>
|
<p>@_localizer[WebKey.Formats.LockedBody.Format(codeKeyName)].Value.Format(qrCodeExpiration.ToString())</p>
|
||||||
</section>
|
</section>
|
||||||
<div class="row m-0 p-0">
|
<div class="row m-0 p-0">
|
||||||
<div class="access-code-panel justify-content-center align-items-center p-0 m-0">
|
<div class="access-code-panel justify-content-center align-items-center p-0 m-0">
|
||||||
<form id="form-access-code" class="form form-floating mb-0" method="post">
|
<form id="form-access-code" class="form form-floating mb-0" method="post">
|
||||||
<div class="form-floating access-code-form-floating">
|
<div class="form-floating access-code-form-floating">
|
||||||
<input type="password" id="access_code" class="form-control" name="@accessCodeName" placeholder="@_localizer[viaSms ? WebKey.LockedSmsAccessCode : WebKey.LockedAccessCode]" required="required">
|
<input type="password" id="access_code" class="form-control" name="@codeType" placeholder="@_localizer[WebKey.Formats.LockedCodeLabel.Format(codeKeyName)]" required="required">
|
||||||
<label for="access_code">@_localizer[viaSms ? WebKey.LockedSmsAccessCode : WebKey.LockedAccessCode]</label>
|
<label for="access_code">@_localizer[WebKey.Formats.LockedCodeLabel.Format(codeKeyName)]</label>
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
<span class="material-symbols-outlined">
|
<span class="material-symbols-outlined">
|
||||||
login
|
login
|
||||||
@@ -57,7 +59,7 @@
|
|||||||
<label class="form-check-label" for="flexSwitchCheckChecked">2FA per SMS</label>
|
<label class="form-check-label" for="flexSwitchCheckChecked">2FA per SMS</label>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if (expiration is not null)
|
@if (smsExpiration is not null)
|
||||||
{
|
{
|
||||||
<div id="sms-timer" class="alert alert-primary" role="alert">00:00</div>
|
<div id="sms-timer" class="alert alert-primary" role="alert">00:00</div>
|
||||||
}
|
}
|
||||||
@@ -74,13 +76,13 @@
|
|||||||
}
|
}
|
||||||
<section class="no-receiver-explanation text-center">
|
<section class="no-receiver-explanation text-center">
|
||||||
<details>
|
<details>
|
||||||
<summary>@_localizer[viaSms ? WebKey.LockedSmsTfaFooterTitle : WebKey.LockedFooterTitle]</summary>
|
<summary>@_localizer[WebKey.Formats.LockedFooterTitle.Format(codeKeyName)]</summary>
|
||||||
<p>@_localizer[viaSms ? WebKey.LockedSmsTfaFooterBody : WebKey.LockedFooterBody]</p>
|
<p>@_localizer[WebKey.Formats.LockedFooterBody.Format(codeKeyName)]</p>
|
||||||
</details>
|
</details>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<script nonce="@nonce">
|
<script nonce="@nonce">
|
||||||
var expiration = new Date(@Html.Raw(JsonConvert.SerializeObject(expiration)));
|
var expiration = new Date(@Html.Raw(JsonConvert.SerializeObject(smsExpiration)));
|
||||||
|
|
||||||
const element = document.getElementById("sms-timer");
|
const element = document.getElementById("sms-timer");
|
||||||
|
|
||||||
|
|||||||
@@ -10,17 +10,6 @@
|
|||||||
public static readonly string NonDecodableEnvelopeReceiverId = nameof(NonDecodableEnvelopeReceiverId);
|
public static readonly string NonDecodableEnvelopeReceiverId = nameof(NonDecodableEnvelopeReceiverId);
|
||||||
public static readonly string de_DE = nameof(de_DE).Replace("_", "-");
|
public static readonly string de_DE = nameof(de_DE).Replace("_", "-");
|
||||||
public static readonly string en_US = nameof(en_US).Replace("_", "-");
|
public static readonly string en_US = nameof(en_US).Replace("_", "-");
|
||||||
public static readonly string LockedTitle = nameof(LockedTitle);
|
|
||||||
public static readonly string LockedSmsTfaTitle = nameof(LockedSmsTfaTitle);
|
|
||||||
public static readonly string LockedBody = nameof(LockedBody);
|
|
||||||
public static readonly string LockedSmsTfaBody = nameof(LockedSmsTfaBody);
|
|
||||||
public static readonly string LocakedOpen = nameof(LocakedOpen);
|
|
||||||
public static readonly string LockedAccessCode = nameof(LockedAccessCode);
|
|
||||||
public static readonly string LockedSmsAccessCode = nameof(LockedSmsAccessCode);
|
|
||||||
public static readonly string LockedFooterTitle = nameof(LockedFooterTitle);
|
|
||||||
public static readonly string LockedSmsTfaFooterTitle = nameof(LockedSmsTfaFooterTitle);
|
|
||||||
public static readonly string LockedFooterBody = nameof(LockedFooterBody);
|
|
||||||
public static readonly string LockedSmsTfaFooterBody = nameof(LockedSmsTfaFooterBody);
|
|
||||||
public static readonly string WrongAccessCode = nameof(WrongAccessCode);
|
public static readonly string WrongAccessCode = nameof(WrongAccessCode);
|
||||||
public static readonly string SignDoc = nameof(SignDoc);
|
public static readonly string SignDoc = nameof(SignDoc);
|
||||||
public static readonly string DocRejected = nameof(DocRejected);
|
public static readonly string DocRejected = nameof(DocRejected);
|
||||||
@@ -42,5 +31,22 @@
|
|||||||
public static readonly string ViewDoc = nameof(ViewDoc);
|
public static readonly string ViewDoc = nameof(ViewDoc);
|
||||||
public static readonly string HomePageDescription = nameof(HomePageDescription);
|
public static readonly string HomePageDescription = nameof(HomePageDescription);
|
||||||
public static readonly string Privacy = nameof(Privacy);
|
public static readonly string Privacy = nameof(Privacy);
|
||||||
|
|
||||||
|
public static class Formats
|
||||||
|
{
|
||||||
|
public static readonly string LockedTitle = nameof(LockedTitle) + "{0}";
|
||||||
|
|
||||||
|
public static readonly string LockedBody = nameof(LockedBody) + "{0}";
|
||||||
|
|
||||||
|
public static readonly string LockedCodeLabel = nameof(LockedCodeLabel) + "{0}";
|
||||||
|
|
||||||
|
public static readonly string LockedFooterTitle = nameof(LockedFooterTitle) + "{0}";
|
||||||
|
|
||||||
|
public static readonly string LockedFooterBody = nameof(LockedFooterBody) + "{0}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Format(this string st, object? arg0) => string.Format(st, arg0: arg0);
|
||||||
|
|
||||||
|
public static string Format(this string st, params object?[] args) => string.Format(st, args: args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,7 +225,7 @@ footer {
|
|||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page header .icon.locked.sms-tfa {
|
.page header .icon.locked.tfa {
|
||||||
background-color: #ff7207;
|
background-color: #ff7207;
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user