1
0
mirror of https://github.com/bitwarden/server synced 2025-12-28 22:23:30 +00:00

[PM-5645] Cosmos DB Grant Storage (#3634)

* table storage grants

* simple shard on storage accounts

* use is not

* cosmos grant repo

* remove single storage connection string

* some fixes to dapper grant repo

* pattern matching

* add fallback to base PersistedGrantStore

* service collection extension cleanup

* cleanup

* remove unused Id

* empty string rowkey

* fix sharding method logic

* ttl for cosmos

* make ttl an int

* fixes to cosmos implementation

* fix partition key values

* catch notfound exceptions

* indenting

* update grantitem with custom serialization

* use new transform helpers

* grantloader perf test tool

* ref

* remove grant loader project

* remove table storage implementation

* remove table storage stuff

* all redis fallback to build to null

* revert sln file change

* EOF new line

* remove trailing comma

* lint fixes

* add grant to names

* move cosmos serilaizer to utils

* add some .net 8 keyed service comments

* EnableContentResponseOnWrite

* Fix type in EF grant repository
This commit is contained in:
Kyle Spearrin
2024-01-10 07:59:16 -05:00
committed by GitHub
parent 03cbc7983b
commit a6db79f613
15 changed files with 411 additions and 66 deletions

View File

@@ -1,18 +1,24 @@
using Bit.Core.Auth.Repositories;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Auth.Repositories;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Stores;
using Grant = Bit.Core.Auth.Entities.Grant;
namespace Bit.Identity.IdentityServer;
public class PersistedGrantStore : IPersistedGrantStore
{
private readonly IGrantRepository _grantRepository;
private readonly Func<PersistedGrant, IGrant> _toGrant;
private readonly IPersistedGrantStore _fallbackGrantStore;
public PersistedGrantStore(
IGrantRepository grantRepository)
IGrantRepository grantRepository,
Func<PersistedGrant, IGrant> toGrant,
IPersistedGrantStore fallbackGrantStore = null)
{
_grantRepository = grantRepository;
_toGrant = toGrant;
_fallbackGrantStore = fallbackGrantStore;
}
public async Task<PersistedGrant> GetAsync(string key)
@@ -20,6 +26,11 @@ public class PersistedGrantStore : IPersistedGrantStore
var grant = await _grantRepository.GetByKeyAsync(key);
if (grant == null)
{
if (_fallbackGrantStore != null)
{
// It wasn't found, there is a chance is was instead stored in the fallback store
return await _fallbackGrantStore.GetAsync(key);
}
return null;
}
@@ -47,28 +58,11 @@ public class PersistedGrantStore : IPersistedGrantStore
public async Task StoreAsync(PersistedGrant pGrant)
{
var grant = ToGrant(pGrant);
var grant = _toGrant(pGrant);
await _grantRepository.SaveAsync(grant);
}
private Grant ToGrant(PersistedGrant pGrant)
{
return new Grant
{
Key = pGrant.Key,
Type = pGrant.Type,
SubjectId = pGrant.SubjectId,
SessionId = pGrant.SessionId,
ClientId = pGrant.ClientId,
Description = pGrant.Description,
CreationDate = pGrant.CreationTime,
ExpirationDate = pGrant.Expiration,
ConsumedDate = pGrant.ConsumedTime,
Data = pGrant.Data
};
}
private PersistedGrant ToPersistedGrant(Grant grant)
private PersistedGrant ToPersistedGrant(IGrant grant)
{
return new PersistedGrant
{

View File

@@ -1,4 +1,5 @@
using Bit.Core.IdentityServer;
using Bit.Core.Auth.Repositories;
using Bit.Core.IdentityServer;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Bit.Identity.IdentityServer;
@@ -51,31 +52,58 @@ public static class ServiceCollectionExtensions
.AddIdentityServerCertificate(env, globalSettings)
.AddExtensionGrantValidator<WebAuthnGrantValidator>();
if (CoreHelpers.SettingHasValue(globalSettings.IdentityServer.RedisConnectionString))
if (CoreHelpers.SettingHasValue(globalSettings.IdentityServer.CosmosConnectionString))
{
// If we have redis, prefer it
// Add the original persisted grant store via it's implementation type
// so we can inject it right after.
services.AddSingleton<PersistedGrantStore>();
services.AddSingleton<IPersistedGrantStore>(sp =>
{
return new RedisPersistedGrantStore(
// TODO: .NET 8 create a keyed service for this connection multiplexer and even PersistedGrantStore
ConnectionMultiplexer.Connect(globalSettings.IdentityServer.RedisConnectionString),
sp.GetRequiredService<ILogger<RedisPersistedGrantStore>>(),
sp.GetRequiredService<PersistedGrantStore>() // Fallback grant store
);
});
services.AddSingleton<IPersistedGrantStore>(sp => BuildCosmosGrantStore(sp, globalSettings));
}
else if (CoreHelpers.SettingHasValue(globalSettings.IdentityServer.RedisConnectionString))
{
services.AddSingleton<IPersistedGrantStore>(sp => BuildRedisGrantStore(sp, globalSettings));
}
else
{
// Use the original grant store
identityServerBuilder.AddPersistedGrantStore<PersistedGrantStore>();
services.AddTransient<IPersistedGrantStore>(sp => BuildSqlGrantStore(sp));
}
services.AddTransient<ICorsPolicyService, CustomCorsPolicyService>();
return identityServerBuilder;
}
private static PersistedGrantStore BuildCosmosGrantStore(IServiceProvider sp, GlobalSettings globalSettings)
{
if (!CoreHelpers.SettingHasValue(globalSettings.IdentityServer.CosmosConnectionString))
{
throw new ArgumentException("No cosmos config string available.");
}
return new PersistedGrantStore(
// TODO: Perhaps we want to evaluate moving this repo to DI as a keyed service singleton in .NET 8
new Core.Auth.Repositories.Cosmos.GrantRepository(globalSettings),
g => new Core.Auth.Models.Data.GrantItem(g),
fallbackGrantStore: BuildRedisGrantStore(sp, globalSettings, true));
}
private static RedisPersistedGrantStore BuildRedisGrantStore(IServiceProvider sp,
GlobalSettings globalSettings, bool allowNull = false)
{
if (!CoreHelpers.SettingHasValue(globalSettings.IdentityServer.RedisConnectionString))
{
if (allowNull)
{
return null;
}
throw new ArgumentException("No redis config string available.");
}
return new RedisPersistedGrantStore(
// TODO: .NET 8 create a keyed service for this connection multiplexer and even PersistedGrantStore
ConnectionMultiplexer.Connect(globalSettings.IdentityServer.RedisConnectionString),
sp.GetRequiredService<ILogger<RedisPersistedGrantStore>>(),
fallbackGrantStore: BuildSqlGrantStore(sp));
}
private static PersistedGrantStore BuildSqlGrantStore(IServiceProvider sp)
{
return new PersistedGrantStore(sp.GetRequiredService<IGrantRepository>(),
g => new Core.Auth.Entities.Grant(g));
}
}