From 2f9c59632dc4ea6e4a5edde9ddec089cc4f2617e Mon Sep 17 00:00:00 2001 From: MrSmoke <709976+MrSmoke@users.noreply.github.com> Date: Fri, 12 Jan 2024 12:20:48 +1100 Subject: [PATCH] Add InstanceName to RedisUserSessionStore --- .../src/RedisUserSessionCacheOptions.cs | 1 + .../src/RedisUserSessionStore.cs | 24 +-- .../test/RedisUserSessionStoreTests.cs | 141 +++++++----------- 3 files changed, 69 insertions(+), 97 deletions(-) diff --git a/src/AspNetCore/Authentication/StackExchangeRedis/src/RedisUserSessionCacheOptions.cs b/src/AspNetCore/Authentication/StackExchangeRedis/src/RedisUserSessionCacheOptions.cs index 5e0eeb0..cc89790 100644 --- a/src/AspNetCore/Authentication/StackExchangeRedis/src/RedisUserSessionCacheOptions.cs +++ b/src/AspNetCore/Authentication/StackExchangeRedis/src/RedisUserSessionCacheOptions.cs @@ -6,6 +6,7 @@ public class RedisUserSessionCacheOptions { public IConnectionMultiplexer? Connection { get; set; } + public string? InstanceName { get; set; } public void Validate() { diff --git a/src/AspNetCore/Authentication/StackExchangeRedis/src/RedisUserSessionStore.cs b/src/AspNetCore/Authentication/StackExchangeRedis/src/RedisUserSessionStore.cs index d86dc3d..c806b1b 100644 --- a/src/AspNetCore/Authentication/StackExchangeRedis/src/RedisUserSessionStore.cs +++ b/src/AspNetCore/Authentication/StackExchangeRedis/src/RedisUserSessionStore.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Options; using StackExchange.Redis; using System; + using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -11,6 +12,7 @@ public class RedisUserSessionStore : IUserSessionStore { private readonly IDatabase _database; + private readonly RedisKey _keyPrefix; public RedisUserSessionStore(IOptions cacheOptions) { @@ -22,6 +24,10 @@ public RedisUserSessionStore(IOptions cacheOptions if (options.Connection == null) throw new ArgumentException("Connection cannot be null"); _database = options.Connection.GetDatabase(); + + var instanceName = options.InstanceName; + if (!string.IsNullOrEmpty(instanceName)) + _keyPrefix = (RedisKey)Encoding.UTF8.GetBytes(instanceName); } public Task GetAsync(string key, CancellationToken token = default) @@ -72,8 +78,8 @@ public async Task DeleteAsync(string key, CancellationToken token = default) var session = await GetInternalAsync(key, token); var keysToDelete = session?.SessionId == null - ? new RedisKey[] {key} - : new RedisKey[] {key, GetSessionIdKey(session.SessionId)}; + ? new[] { _keyPrefix.Append(key) } + : new[] { _keyPrefix.Append(key), GetSessionIdKey(session.SessionId) }; // Delete both the session and the session id lookup await _database.KeyDeleteAsync(keysToDelete); @@ -89,11 +95,7 @@ public async Task DeleteBySessionIdAsync(string sessionId, CancellationToken tok if (!sessionKey.HasValue) return; - var keys = new RedisKey[] - { - sessionKey.ToString(), - GetSessionIdKey(sessionId) - }; + var keys = new[] { _keyPrefix.Append(sessionKey.ToString()), GetSessionIdKey(sessionId) }; // Delete both the session and the session id lookup await _database.KeyDeleteAsync(keys); @@ -105,15 +107,15 @@ public async Task DeleteBySessionIdAsync(string sessionId, CancellationToken tok token.ThrowIfCancellationRequested(); - var session = await _database.StringGetAsync(key); + var session = await _database.StringGetAsync(_keyPrefix.Append(key)); return session.HasValue ? Deserialize(session!) : null; } - private static void AddInternal(ITransaction transaction, string key, UserSession session) + private void AddInternal(ITransaction transaction, string key, UserSession session) { var expireTimeSpan = session.Expiry?.ToRedisExpiryTimeSpan(); - _ = transaction.StringSetAsync(key, Serialize(session), expireTimeSpan); + _ = transaction.StringSetAsync(_keyPrefix.Append(key), Serialize(session), expireTimeSpan); if (!string.IsNullOrWhiteSpace(session.SessionId)) _ = transaction.StringSetAsync(GetSessionIdKey(session.SessionId), session.Key, expireTimeSpan); @@ -122,6 +124,6 @@ private static void AddInternal(ITransaction transaction, string key, UserSessio private static UserSession? Deserialize(byte[] value) => JsonSerializer.Deserialize(value); private static byte[] Serialize(UserSession userSession) => JsonSerializer.SerializeToUtf8Bytes(userSession); - private static string GetSessionIdKey(string sessionId) => "sessionId:" + sessionId; + private RedisKey GetSessionIdKey(string sessionId) => _keyPrefix.Append("sessionId:" + sessionId); } } diff --git a/src/AspNetCore/Authentication/StackExchangeRedis/test/RedisUserSessionStoreTests.cs b/src/AspNetCore/Authentication/StackExchangeRedis/test/RedisUserSessionStoreTests.cs index b4c7e20..4df4f5e 100644 --- a/src/AspNetCore/Authentication/StackExchangeRedis/test/RedisUserSessionStoreTests.cs +++ b/src/AspNetCore/Authentication/StackExchangeRedis/test/RedisUserSessionStoreTests.cs @@ -18,20 +18,6 @@ public class RedisUserSessionStoreTests [SkipOnCI(SkipCiReason)] public async Task Can_Add_UserSession_To_Redis() { - var services = new ServiceCollection(); - var builder = new OpenIdConnectSessionHandlerBuilder(services); - - //redis - var redis = ConnectionMultiplexer.Connect("localhost:6379"); - - builder.AddStackExchangeRedisUserSessionStore(options => - { - options.Connection = redis; - }); - - // Build - var serviceProvider = services.BuildServiceProvider(); - var session = new UserSession("1", new byte[] {1, 2}) { SessionId = "sessionId1", @@ -39,7 +25,8 @@ public async Task Can_Add_UserSession_To_Redis() Expiry = DateTimeOffset.UtcNow.AddSeconds(10) }; - var sessionStore = serviceProvider.GetRequiredService(); + var sessionStore = await GetUserSessionStoreAsync(); + await sessionStore.AddAsync(session, CancellationToken.None); var retrievedSession = await sessionStore.GetAsync("1", CancellationToken.None); @@ -52,22 +39,34 @@ public async Task Can_Add_UserSession_To_Redis() [SkippableFact] [SkipOnCI(SkipCiReason)] - public async Task Can_Update_UserSession_To_Same_key() + public async Task AddAsync_InstanceName() { - var services = new ServiceCollection(); - var builder = new OpenIdConnectSessionHandlerBuilder(services); - - //redis - var redis = ConnectionMultiplexer.Connect("localhost:6379"); + var session = new UserSession("1", new byte[] {1, 2}) + { + SessionId = "sessionId1", + Subject = "subject1", + Expiry = DateTimeOffset.UtcNow.AddSeconds(10) + }; - builder.AddStackExchangeRedisUserSessionStore(options => + var sessionStore = await GetUserSessionStoreAsync(o => { - options.Connection = redis; + o.InstanceName = "test_"; }); - // Build - var serviceProvider = services.BuildServiceProvider(); + await sessionStore.AddAsync(session, CancellationToken.None); + var retrievedSession = await sessionStore.GetAsync("1", CancellationToken.None); + + Assert.NotNull(retrievedSession); + Assert.Equal(session.Key, retrievedSession.Key); + Assert.Equal(session.Ticket, retrievedSession.Ticket); + Assert.Equal(session.SessionId, retrievedSession.SessionId); + Assert.Equal(session.Subject, retrievedSession.Subject); + } + [SkippableFact] + [SkipOnCI(SkipCiReason)] + public async Task Can_Update_UserSession_To_Same_key() + { var session = new UserSession("2", new byte[] { 3, 4 }) { SessionId = "sessionId2", @@ -75,7 +74,8 @@ public async Task Can_Update_UserSession_To_Same_key() Expiry = DateTimeOffset.UtcNow.AddSeconds(10) }; - var sessionStore = serviceProvider.GetRequiredService(); + var sessionStore = await GetUserSessionStoreAsync(); + await sessionStore.AddAsync(session, CancellationToken.None); var retrievedSession = await sessionStore.GetAsync("2", CancellationToken.None); @@ -104,27 +104,14 @@ public async Task Can_Update_UserSession_To_Same_key() [SkipOnCI(SkipCiReason)] public async Task Can_Delete_UserSession_By_key() { - var services = new ServiceCollection(); - var builder = new OpenIdConnectSessionHandlerBuilder(services); - - //redis - var redis = ConnectionMultiplexer.Connect("localhost:6379"); - - builder.AddStackExchangeRedisUserSessionStore(options => - { - options.Connection = redis; - }); - - // Build - var serviceProvider = services.BuildServiceProvider(); - var session = new UserSession("3", new byte[] { 7, 8 }) { SessionId = "sessionId3", Subject = "subject3" }; - var sessionStore = serviceProvider.GetRequiredService(); + var sessionStore = await GetUserSessionStoreAsync(); + await sessionStore.AddAsync(session, CancellationToken.None); var retrievedSession = await sessionStore.GetAsync("3", CancellationToken.None); @@ -140,27 +127,14 @@ public async Task Can_Delete_UserSession_By_key() [SkipOnCI(SkipCiReason)] public async Task Can_Delete_UserSession_By_SessionId() { - var services = new ServiceCollection(); - var builder = new OpenIdConnectSessionHandlerBuilder(services); - - //redis - var redis = ConnectionMultiplexer.Connect("localhost:6379"); - - builder.AddStackExchangeRedisUserSessionStore(options => - { - options.Connection = redis; - }); - - // Build - var serviceProvider = services.BuildServiceProvider(); - var session = new UserSession("4", new byte[] { 9, 10 }) { SessionId = "sessionId4", Subject = "subject4" }; - var sessionStore = serviceProvider.GetRequiredService(); + var sessionStore = await GetUserSessionStoreAsync(); + await sessionStore.AddAsync(session, CancellationToken.None); var retrievedSession = await sessionStore.GetAsync("4", CancellationToken.None); @@ -176,20 +150,6 @@ public async Task Can_Delete_UserSession_By_SessionId() [SkipOnCI(SkipCiReason)] public async Task Can_Expire_UserSession_Key() { - var services = new ServiceCollection(); - var builder = new OpenIdConnectSessionHandlerBuilder(services); - - //redis - var redis = ConnectionMultiplexer.Connect("localhost:6379"); - - builder.AddStackExchangeRedisUserSessionStore(options => - { - options.Connection = redis; - }); - - // Build - var serviceProvider = services.BuildServiceProvider(); - var session = new UserSession("5", new byte[] { 1, 2 }) { SessionId = "sessionId5", @@ -197,7 +157,8 @@ public async Task Can_Expire_UserSession_Key() Expiry = DateTimeOffset.UtcNow.AddSeconds(1) }; - var sessionStore = serviceProvider.GetRequiredService(); + var sessionStore = await GetUserSessionStoreAsync(); + await sessionStore.AddAsync(session, CancellationToken.None); var retrievedSession = await sessionStore.GetAsync("5", CancellationToken.None); @@ -217,20 +178,6 @@ public async Task Add_Expired_Session_ThrowsException() { await Assert.ThrowsAsync(async () => { - var services = new ServiceCollection(); - var builder = new OpenIdConnectSessionHandlerBuilder(services); - - //redis - var redis = ConnectionMultiplexer.Connect("localhost:6379"); - - builder.AddStackExchangeRedisUserSessionStore(options => - { - options.Connection = redis; - }); - - // Build - var serviceProvider = services.BuildServiceProvider(); - var session = new UserSession("6", new byte[] { 1, 2 }) { SessionId = "sessionId6", @@ -238,9 +185,31 @@ await Assert.ThrowsAsync(async () => Expiry = DateTimeOffset.UtcNow.AddSeconds(-10) }; - var sessionStore = serviceProvider.GetRequiredService(); + var sessionStore = await GetUserSessionStoreAsync(); + await sessionStore.AddAsync(session, CancellationToken.None); }); } + + private static async Task GetUserSessionStoreAsync( + Action? configure = null) + { + var services = new ServiceCollection(); + var builder = new OpenIdConnectSessionHandlerBuilder(services); + + //redis + var redis = await ConnectionMultiplexer.ConnectAsync("localhost:6379"); + + builder.AddStackExchangeRedisUserSessionStore(options => + { + options.Connection = redis; + + configure?.Invoke(options); + }); + + // Build + var serviceProvider = services.BuildServiceProvider(); + return serviceProvider.GetRequiredService(); + } } }