using System; using System.Threading.Tasks; using gehGassi.Core.Interfaces; using gehGassi.Web.Helper; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace gehGassi.Web.Auth { /// /// Verteilter Ticketstore für AuthenticationTickets /// public class DistributedCacheTicketStore : ITicketStore { private readonly IOptions _sessionSettings; private const string KeyPrefix = "AuthSessionStore-"; private readonly IDistributedCache _cache; public DistributedCacheTicketStore(IOptions sessionSettings, IDistributedCache cache) { _sessionSettings = sessionSettings; _cache = cache; } public async Task StoreAsync(AuthenticationTicket ticket) { var key = KeyPrefix + ticket.Principal.Identity.Name; await RenewAsync(key, ticket); return key; } public Task RenewAsync(string key, AuthenticationTicket ticket) { if (!key.StartsWith(KeyPrefix)) key = KeyPrefix + key; var options = new DistributedCacheEntryOptions(); var expiresUtc = ticket.Properties.ExpiresUtc; if (expiresUtc.HasValue) { options.SetAbsoluteExpiration(expiresUtc.Value); } options.SetSlidingExpiration(TimeSpan.FromMinutes(_sessionSettings.Value.TimeOut)); var serializedTicket = TicketSerializer.Default.Serialize(ticket); _cache.Set(key, serializedTicket, options); return Task.FromResult(0); } public Task RetrieveAsync(string key) { if (!key.StartsWith(KeyPrefix)) key = KeyPrefix + key; AuthenticationTicket ticket = null; var storedTicket = _cache.Get(key); if (storedTicket != null) { ticket = TicketSerializer.Default.Deserialize(storedTicket); } return Task.FromResult(ticket); } public Task RemoveAsync(string key) { if (!key.StartsWith(KeyPrefix)) key = KeyPrefix + key; _cache.Remove(key); return Task.FromResult(0); } } /// /// Verteilter Ticketstore für AuthenticationTickets der diese initial von einer DB lädt und wieder speichern kann. /// Übersteht "Server-Resets" /// public class DistributedCachePersistedTicketStore : ITicketStore { private bool _initialized; private IServiceProvider _serivceProvider; private readonly IOptions _sessionSettings; private const string KeyPrefix = "AuthSessionStore-"; private readonly IDistributedCache _cache; public DistributedCachePersistedTicketStore(IOptions sessionSettings, IDistributedCache cache, IServiceProvider serviceProvider) { _sessionSettings = sessionSettings; _cache = cache; _initialized = false; _serivceProvider = serviceProvider; } private async Task InitializeAsync() { using var scope = _serivceProvider.CreateScope(); var persistedTicketService = scope.ServiceProvider.GetRequiredService(); var items = await persistedTicketService.GetAllAsync(); foreach (var item in items) { var options = new DistributedCacheEntryOptions(); var expiresUtc = item.Expires; if (expiresUtc.HasValue) { options.SetAbsoluteExpiration(expiresUtc.Value); } options.SetSlidingExpiration(TimeSpan.FromMinutes(_sessionSettings.Value.TimeOut)); if (await _cache.GetAsync(item.Key) == null) _cache.Set(item.Key, item.Ticket, options); } _initialized = true; } public async Task StoreAsync(AuthenticationTicket ticket) { if (!_initialized) await InitializeAsync(); var key = KeyPrefix + ticket.Principal.Identity.Name; await RenewAsync(key, ticket); return key; } public async Task RenewAsync(string key, AuthenticationTicket ticket) { if (!_initialized) await InitializeAsync(); if (!key.StartsWith(KeyPrefix)) key = KeyPrefix + key; var options = new DistributedCacheEntryOptions(); var expiresUtc = ticket.Properties.ExpiresUtc; if (expiresUtc.HasValue) { options.SetAbsoluteExpiration(expiresUtc.Value); } options.SetSlidingExpiration(TimeSpan.FromMinutes(_sessionSettings.Value.TimeOut)); var serializedTicket = TicketSerializer.Default.Serialize(ticket); _cache.Set(key, serializedTicket, options); using var scope = _serivceProvider.CreateScope(); var persistedTicketService = scope.ServiceProvider.GetRequiredService(); var userId = key.Replace(KeyPrefix, ""); var expires = ticket.Properties.ExpiresUtc; var byteTicket = SerializeToBytes(ticket); await persistedTicketService.SaveAsync(key, userId, byteTicket, expires); await persistedTicketService.CommitAsync("System"); } public async Task RetrieveAsync(string key) { if (!_initialized) await InitializeAsync(); if (!key.StartsWith(KeyPrefix)) key = KeyPrefix + key; AuthenticationTicket ticket = null; var storedTicket = _cache.Get(key); if (storedTicket != null) { ticket = TicketSerializer.Default.Deserialize(storedTicket); } return ticket; } public async Task RemoveAsync(string key) { if (!_initialized) await InitializeAsync(); if (!key.StartsWith(KeyPrefix)) key = KeyPrefix + key; using var scope = _serivceProvider.CreateScope(); var persistedTicketService = scope.ServiceProvider.GetRequiredService(); var ticket = await RetrieveAsync(key); _cache.Remove(key); await persistedTicketService.RemoveAsync(key); await persistedTicketService.CommitAsync("System"); } private byte[] SerializeToBytes(AuthenticationTicket source) => TicketSerializer.Default.Serialize(source); private AuthenticationTicket DeserializeFromBytes(byte[] source) => source == null ? null : TicketSerializer.Default.Deserialize(source); } }