feat(GtxMessagingService): Zwischenspeicherung für SMS-Code und Ablauf des SMS-Codes mittels Envelope-Receiver-ID hinzugefügt

- Erweiterungsmethode für Zeitcaching hinzugefügt.
This commit is contained in:
Developer 02
2024-11-29 16:25:20 +01:00
parent 2a963a1861
commit cdec5485c6
10 changed files with 97 additions and 9 deletions

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Abstractions.Client; using DigitalData.Core.Abstractions.Client;
using Microsoft.Extensions.Caching.Distributed;
namespace EnvelopeGenerator.Application.Configurations.GtxMessaging namespace EnvelopeGenerator.Application.Configurations.GtxMessaging
{ {
@@ -20,5 +21,19 @@ namespace EnvelopeGenerator.Application.Configurations.GtxMessaging
public string MessageQueryParamName { get; init; } = "text"; public string MessageQueryParamName { get; init; } = "text";
public int CodeLength { get; init; } = 5; public int CodeLength { get; init; } = 5;
/// <summary>
/// Gets the cache key format for SMS codes.
/// The placeholder {0} represents the envelopeReceiverId.
/// </summary>
public string CodeCacheKeyFormat { get; init; } = "sms-code-{0}";
/// <summary>
/// Gets the cache expiration key format for SMS codes.
/// The placeholder {0} represents the envelopeReceiverId.
/// </summary>
public string CodeExpirationCacheKeyFormat { get; init; } = "sms-code-expiration-{0}";
public TimeSpan CodeCacheValidityPeriod { get; init; } = new(0, 5, 0);
} }
} }

View File

@@ -8,6 +8,6 @@ namespace EnvelopeGenerator.Application.Contracts
Task<SmsResponse> SendSmsAsync(string recipient, string message); Task<SmsResponse> SendSmsAsync(string recipient, string message);
Task<SmsResponse> SendSmsCodeAsync(string recipient); Task<SmsResponse> SendSmsCodeAsync(string recipient, string envelopeReceiverId);
} }
} }

View File

@@ -0,0 +1,43 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EnvelopeGenerator.Application.Extensions
{
public static class CacheExtensions
{
public static Task SetLongAsync(this IDistributedCache cache, string key, long value, DistributedCacheEntryOptions? options = null)
=> options is null
? cache.SetAsync(key, BitConverter.GetBytes(value))
: cache.SetAsync(key, BitConverter.GetBytes(value), options: options);
public static async Task<long?> GetLongAsync(this IDistributedCache cache, string key)
{
var value = await cache.GetAsync(key);
return value is null ? null : BitConverter.ToInt64(value, 0);
}
public static Task SetDateTimeAsync(this IDistributedCache cache, string key, DateTime value, DistributedCacheEntryOptions? options = null)
=> cache.SetLongAsync(key: key, value: value.Ticks, options: options);
public static async Task<DateTime?> GetDateTimeAsync(this IDistributedCache cache, string key)
{
var value = await cache.GetAsync(key);
return value is null ? null : new(BitConverter.ToInt64(value, 0));
}
public static Task SetTimeSpanAsync(this IDistributedCache cache, string key, TimeSpan value, DistributedCacheEntryOptions? options = null)
=> cache.SetLongAsync(key: key, value: value.Ticks, options: options);
public static async Task<TimeSpan?> GetTimeSpanAsync(this IDistributedCache cache, string key)
{
var value = await cache.GetAsync(key);
return value is null ? null : new(BitConverter.ToInt64(value, 0));
}
}
}

View File

@@ -11,7 +11,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using DigitalData.Core.Client; using DigitalData.Core.Client;
using EnvelopeGenerator.Application.Configurations.GtxMessaging; using EnvelopeGenerator.Application.Configurations.GtxMessaging;
namespace EnvelopeGenerator.Application namespace EnvelopeGenerator.Application.Extensions
{ {
public static class DIExtensions public static class DIExtensions
{ {

View File

@@ -1,6 +1,6 @@
using EnvelopeGenerator.Domain.HttpResponse; using EnvelopeGenerator.Domain.HttpResponse;
namespace EnvelopeGenerator.Application namespace EnvelopeGenerator.Application.Extensions
{ {
public static class MappingExtensions public static class MappingExtensions
{ {

View File

@@ -5,6 +5,7 @@ using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly; using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
using EnvelopeGenerator.Application.DTOs.Messaging; using EnvelopeGenerator.Application.DTOs.Messaging;
using EnvelopeGenerator.Application.DTOs.Receiver; using EnvelopeGenerator.Application.DTOs.Receiver;
using EnvelopeGenerator.Application.Extensions;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Domain.HttpResponse; using EnvelopeGenerator.Domain.HttpResponse;

View File

@@ -4,7 +4,9 @@ using DigitalData.Core.Client;
using EnvelopeGenerator.Application.Configurations.GtxMessaging; using EnvelopeGenerator.Application.Configurations.GtxMessaging;
using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs.Messaging; using EnvelopeGenerator.Application.DTOs.Messaging;
using EnvelopeGenerator.Application.Extensions;
using EnvelopeGenerator.Domain.HttpResponse; using EnvelopeGenerator.Domain.HttpResponse;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace EnvelopeGenerator.Application.Services namespace EnvelopeGenerator.Application.Services
@@ -19,15 +21,21 @@ namespace EnvelopeGenerator.Application.Services
private readonly ICodeGenerator _codeGen; private readonly ICodeGenerator _codeGen;
private readonly IDistributedCache _cache;
public string ServiceProvider { get; } public string ServiceProvider { get; }
public GtxMessagingService(IHttpClientService<SmsParams> smsClient, IOptions<SmsParams> smsParamsOptions, IMapper mapper, ICodeGenerator codeGenerator) private readonly DistributedCacheEntryOptions _codeCacheOptions;
public GtxMessagingService(IHttpClientService<SmsParams> smsClient, IOptions<SmsParams> smsParamsOptions, IMapper mapper, ICodeGenerator codeGenerator, IDistributedCache distributedCache)
{ {
_smsClient = smsClient; _smsClient = smsClient;
_smsParams = smsParamsOptions.Value; _smsParams = smsParamsOptions.Value;
_mapper = mapper; _mapper = mapper;
ServiceProvider = GetType().Name.Replace("Service", string.Empty); ServiceProvider = GetType().Name.Replace("Service", string.Empty);
_codeGen = codeGenerator; _codeGen = codeGenerator;
_cache = distributedCache;
_codeCacheOptions = new() { AbsoluteExpirationRelativeToNow = smsParamsOptions.Value.CodeCacheValidityPeriod };
} }
public async Task<SmsResponse> SendSmsAsync(string recipient, string message) public async Task<SmsResponse> SendSmsAsync(string recipient, string message)
@@ -41,10 +49,30 @@ namespace EnvelopeGenerator.Application.Services
.ThenAsync(_mapper.Map<SmsResponse>); .ThenAsync(_mapper.Map<SmsResponse>);
} }
public async Task<SmsResponse> SendSmsCodeAsync(string recipient) public async Task<SmsResponse> SendSmsCodeAsync(string recipient, string envelopeReceiverId)
{ {
var code = _codeGen.GenerateCode(_smsParams.CodeLength); var code_expiration_key = string.Format(_smsParams.CodeExpirationCacheKeyFormat, envelopeReceiverId);
return await SendSmsAsync(recipient: recipient, message: code);
var code_key = string.Format(_smsParams.CodeCacheKeyFormat, envelopeReceiverId);
var code = await _cache.GetStringAsync(code_key);
if (code is null)
{
code = _codeGen.GenerateCode(_smsParams.CodeLength);
await _cache.SetStringAsync(code_key, code, _codeCacheOptions);
await _cache.SetDateTimeAsync(code_expiration_key, DateTime.Now + _smsParams.CodeCacheValidityPeriod, _codeCacheOptions);
return await SendSmsAsync(recipient: recipient, message: code);
}
else
{
var code_expiration = await _cache.GetDateTimeAsync(code_expiration_key);
return code_expiration is null
? new() { Ok = false }
: new() { Ok = false, AllowedAt = code_expiration };
}
} }
} }
} }

View File

@@ -1,7 +1,7 @@
using DigitalData.Core.API; using DigitalData.Core.API;
using DigitalData.Core.Application; using DigitalData.Core.Application;
using DigitalData.UserManager.Application; using DigitalData.UserManager.Application;
using EnvelopeGenerator.Application; using EnvelopeGenerator.Application.Extensions;
using EnvelopeGenerator.Infrastructure; using EnvelopeGenerator.Infrastructure;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Localization;

View File

@@ -190,7 +190,7 @@ namespace EnvelopeGenerator.Web.Controllers
//check if the user has phone is added //check if the user has phone is added
if (er_secret.HasPhoneNumber) if (er_secret.HasPhoneNumber)
{ {
var res = await _msgService.SendSmsCodeAsync(er_secret.PhoneNumber!); var res = await _msgService.SendSmsCodeAsync(er_secret.PhoneNumber!, envelopeReceiverId: envelopeReceiverId);
if(res.Ok) if(res.Ok)
return View("EnvelopeLocked").WithData("ViaSms", true); return View("EnvelopeLocked").WithData("ViaSms", true);
else else

View File

@@ -15,6 +15,7 @@ using EnvelopeGenerator.Application;
using DigitalData.EmailProfilerDispatcher; using DigitalData.EmailProfilerDispatcher;
using EnvelopeGenerator.Infrastructure; using EnvelopeGenerator.Infrastructure;
using EnvelopeGenerator.Web.Sanitizers; using EnvelopeGenerator.Web.Sanitizers;
using EnvelopeGenerator.Application.Extensions;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Info("Logging initialized!"); logger.Info("Logging initialized!");