Skip to content

Commit

Permalink
Add InstanceName to RedisUserSessionStore
Browse files Browse the repository at this point in the history
  • Loading branch information
MrSmoke committed Jan 12, 2024
1 parent 7812b7c commit 2f9c596
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
public class RedisUserSessionCacheOptions
{
public IConnectionMultiplexer? Connection { get; set; }
public string? InstanceName { get; set; }

public void Validate()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using System;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

public class RedisUserSessionStore : IUserSessionStore
{
private readonly IDatabase _database;
private readonly RedisKey _keyPrefix;

public RedisUserSessionStore(IOptions<RedisUserSessionCacheOptions> cacheOptions)
{
Expand All @@ -22,6 +24,10 @@ public RedisUserSessionStore(IOptions<RedisUserSessionCacheOptions> 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<UserSession?> GetAsync(string key, CancellationToken token = default)
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -122,6 +124,6 @@ private static void AddInternal(ITransaction transaction, string key, UserSessio
private static UserSession? Deserialize(byte[] value) => JsonSerializer.Deserialize<UserSession>(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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,15 @@ 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",
Subject = "subject1",
Expiry = DateTimeOffset.UtcNow.AddSeconds(10)
};

var sessionStore = serviceProvider.GetRequiredService<IUserSessionStore>();
var sessionStore = await GetUserSessionStoreAsync();

await sessionStore.AddAsync(session, CancellationToken.None);
var retrievedSession = await sessionStore.GetAsync("1", CancellationToken.None);

Expand All @@ -52,30 +39,43 @@ 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",
Subject = "subject2",
Expiry = DateTimeOffset.UtcNow.AddSeconds(10)
};

var sessionStore = serviceProvider.GetRequiredService<IUserSessionStore>();
var sessionStore = await GetUserSessionStoreAsync();

await sessionStore.AddAsync(session, CancellationToken.None);
var retrievedSession = await sessionStore.GetAsync("2", CancellationToken.None);

Expand Down Expand Up @@ -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<IUserSessionStore>();
var sessionStore = await GetUserSessionStoreAsync();

await sessionStore.AddAsync(session, CancellationToken.None);
var retrievedSession = await sessionStore.GetAsync("3", CancellationToken.None);

Expand All @@ -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<IUserSessionStore>();
var sessionStore = await GetUserSessionStoreAsync();

await sessionStore.AddAsync(session, CancellationToken.None);
var retrievedSession = await sessionStore.GetAsync("4", CancellationToken.None);

Expand All @@ -176,28 +150,15 @@ 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",
Subject = "subject5",
Expiry = DateTimeOffset.UtcNow.AddSeconds(1)
};

var sessionStore = serviceProvider.GetRequiredService<IUserSessionStore>();
var sessionStore = await GetUserSessionStoreAsync();

await sessionStore.AddAsync(session, CancellationToken.None);
var retrievedSession = await sessionStore.GetAsync("5", CancellationToken.None);

Expand All @@ -217,30 +178,38 @@ public async Task Add_Expired_Session_ThrowsException()
{
await Assert.ThrowsAsync<Exception>(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",
Subject = "subject6",
Expiry = DateTimeOffset.UtcNow.AddSeconds(-10)
};

var sessionStore = serviceProvider.GetRequiredService<IUserSessionStore>();
var sessionStore = await GetUserSessionStoreAsync();

await sessionStore.AddAsync(session, CancellationToken.None);
});
}

private static async Task<IUserSessionStore> GetUserSessionStoreAsync(
Action<RedisUserSessionCacheOptions>? 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<IUserSessionStore>();
}
}
}

0 comments on commit 2f9c596

Please sign in to comment.