Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature(oidc): optimize OIDC #408

Merged
merged 3 commits into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,9 @@ public static IServiceCollection AddOidcCacheStorage(this IServiceCollection ser
return services;
}

public static IServiceCollection AddOidcCacheStorage(this IServiceCollection services, RedisConfigurationOptions options)
public static IServiceCollection AddOidcCacheStorage(this IServiceCollection services, RedisConfigurationOptions? options = null)
{
services.AddOidcCache(options);
services.AddOidcCacheStorage();

return services;
}

static IServiceCollection AddOidcCacheStorage(this IServiceCollection services)
{
services.AddSingleton<IClientStore, ClientStore>();
services.AddSingleton<IResourceStore, ResourceStore>();
services.AddSingleton<IPersistedGrantStore, PersistedGrantStore>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ public static IServiceCollection AddOidcCache(this IServiceCollection services,
return services;
}

public static IServiceCollection AddOidcCache(this IServiceCollection services, RedisConfigurationOptions options)
public static IServiceCollection AddOidcCache(this IServiceCollection services, RedisConfigurationOptions? options = null)
{
services.AddMultilevelCache(
Constants.DEFAULT_CLIENT_NAME,
distributedCacheOptions => distributedCacheOptions.UseStackExchangeRedisCache(options),
distributedCacheOptions =>
{
if (options is null) distributedCacheOptions.UseStackExchangeRedisCache();
else distributedCacheOptions.UseStackExchangeRedisCache(options);
},
multilevelCacheOptions =>
{
multilevelCacheOptions.SubscribeKeyType = SubscribeKeyType.SpecificPrefix;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,73 +5,112 @@ namespace Masa.Contrib.Authentication.OpenIdConnect.EFCore.Caches;

public class SyncCache
{
IClientCache _clientCache;
IApiResourceCache _apiResourceCache;
IApiScopeCache _apiScopeCache;
IIdentityResourceCache _identityResourceCache;
IClientCache? _clientCache;
IApiResourceCache? _apiResourceCache;
IApiScopeCache? _apiScopeCache;
IIdentityResourceCache? _identityResourceCache;
DbContext _context;

public SyncCache(IClientCache clientCache, IApiResourceCache apiResourceCache, IApiScopeCache apiScopeCache, IIdentityResourceCache identityResourceCache, OidcDbContext context)
public SyncCache(OidcDbContext context, IServiceProvider serviceProvider)
{
_clientCache = clientCache;
_apiResourceCache = apiResourceCache;
_apiScopeCache = apiScopeCache;
_identityResourceCache = identityResourceCache;
_clientCache = serviceProvider.GetService<IClientCache>();
_apiResourceCache = serviceProvider.GetService<IApiResourceCache>();
_apiScopeCache = serviceProvider.GetService<IApiScopeCache>();
_identityResourceCache = serviceProvider.GetService<IIdentityResourceCache>();
_context = context;
}

public async Task SyncApiResourceCacheAsync(Guid id)
{
if (_apiResourceCache is null) return;

var apiResource = await ApiResourceQuery().FirstOrDefaultAsync(apiResource => apiResource.Id == id);
if (apiResource is null) return;
await _apiResourceCache.SetAsync(apiResource);
}

public async Task SyncApiScopeCacheAsync(Guid id)
{
if (_apiScopeCache is null) return;

var apiScope = await ApiScopeQuery().FirstOrDefaultAsync(apiScope => apiScope.Id == id);
if (apiScope is null) return;
await _apiScopeCache.SetAsync(apiScope);
}

public async Task SyncIdentityResourceCacheAsync(params Guid[] ids)
{
if (_identityResourceCache is null) return;

var identityResources = await IdentityResourceQuery().Where(idrs => ids.Contains(idrs.Id)).ToListAsync();
if (identityResources.Count == 0) return;
await _identityResourceCache.SetRangeAsync(identityResources);
}

public async Task SyncClientCacheAsync(Guid id)
{
if (_clientCache is null) return;

var client = await ClientQuery().FirstOrDefaultAsync(client => client.Id == id);
if (client is null) return;
await _clientCache.SetAsync(client);
}

public async Task RemoveApiResourceCacheAsync(ApiResource apiResource)
{
if (_apiResourceCache is null) return;

await _apiResourceCache.RemoveAsync(apiResource);
}

public async Task RemoveApiScopeCacheAsync(ApiScope apiScope)
{
if (_apiScopeCache is null) return;

await _apiScopeCache.RemoveAsync(apiScope);
}

public async Task RemoveIdentityResourceCacheAsync(IdentityResource identityResource)
{
if (_identityResourceCache is null) return;

await _identityResourceCache.RemoveAsync(identityResource);
}

public async Task RemoveClientCacheAsync(Client client)
{
if (_clientCache is null) return;

await _clientCache.RemoveAsync(client);
}

public async Task ResetAsync()
{
var clients = await ClientQueryAsync();
var apiScopes = await ApiScopeQuery().ToListAsync();
var apiResources = await ApiResourceQuery().ToListAsync();
var identityResource = await IdentityResourceQuery().ToListAsync();

await _clientCache.ResetAsync(clients);
await _apiScopeCache.ResetAsync(apiScopes);
await _apiResourceCache.ResetAsync(apiResources);
await _identityResourceCache.ResetAsync(identityResource);
if (_clientCache is not null)
{
var clients = await ClientQueryAsync();
await _clientCache.ResetAsync(clients);
}
if (_apiScopeCache is not null)
{
var apiScopes = await ApiScopeQuery().ToListAsync();
await _apiScopeCache.ResetAsync(apiScopes);
}
if (_apiResourceCache is not null)
{
var apiResources = await ApiResourceQuery().ToListAsync();
await _apiResourceCache.ResetAsync(apiResources);
}
if (_identityResourceCache is not null)
{
var identityResource = await IdentityResourceQuery().ToListAsync();
await _identityResourceCache.ResetAsync(identityResource);
}
}

public async Task<IEnumerable<Client>> ClientQueryAsync()
{
var clients = await _context.Set<Client>().ToListAsync();
var clients = await _context.Set<Client>().ToListAsync();
var clientPropertys = await _context.Set<ClientProperty>().ToListAsync();
var clientClaims = await _context.Set<ClientClaim>().ToListAsync();
var clientIdPRestrictions = await _context.Set<ClientIdPRestriction>().ToListAsync();
Expand All @@ -82,7 +121,7 @@ public async Task<IEnumerable<Client>> ClientQueryAsync()
var clientPostLogoutRedirectUris = await _context.Set<ClientPostLogoutRedirectUri>().ToListAsync();
var clientScopes = await _context.Set<ClientScope>().ToListAsync();

foreach(var client in clients)
foreach (var client in clients)
{
client.AllowedGrantTypes.AddRange(clientGrantTypes.Where(item => item.ClientId == client.Id));
client.RedirectUris.AddRange(clientRedirectUris.Where(item => item.ClientId == client.Id));
Expand Down Expand Up @@ -123,4 +162,19 @@ private IQueryable<ApiResource> ApiResourceQuery()
.Include(apiResource => apiResource.ApiScopes)
.ThenInclude(apiScope => apiScope.ApiScope);
}

private IQueryable<Client> ClientQuery()
{
return _context.Set<Client>()
.Include(c => c.AllowedGrantTypes)
.Include(c => c.RedirectUris)
.Include(c => c.PostLogoutRedirectUris)
.Include(c => c.Properties)
.Include(c => c.Claims)
.Include(c => c.IdentityProviderRestrictions)
.Include(c => c.AllowedCorsOrigins)
.Include(c => c.ClientSecrets)
.Include(c => c.AllowedScopes)
.AsSplitQuery();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) MASA Stack All rights reserved.
// Licensed under the MIT License. See LICENSE.txt in the project root for license information.

namespace Masa.Contrib.Authentication.OpenIdConnect.EFCore;

public static class OpenIdConnectEFCore
{
public static Assembly Assembly => typeof(OpenIdConnectEFCore).Assembly;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ namespace Masa.Contrib.Authentication.OpenIdConnect.EFCore.Repositories;

public class ClientRepository : IClientRepository
{
IClientCache _cache;
SyncCache _cache;
DbContext _context;
IRepository<Client> _repository;

public ClientRepository(IClientCache cache, OidcDbContext context, IRepository<Client> repository)
public ClientRepository(SyncCache cache, OidcDbContext context, IRepository<Client> repository)
{
_cache = cache;
_context = context;
Expand Down Expand Up @@ -70,24 +70,22 @@ public async ValueTask<Client> AddAsync(Client client)
{
var newClient = await _repository.AddAsync(client);
await _context.SaveChangesAsync();
var detail = await GetDetailAsync(client.Id);
await _cache.SetAsync(detail!);
await _cache.SyncClientCacheAsync(client.Id);
return newClient;
}

public async Task<Client> UpdateAsync(Client client)
{
var newClient = await _repository.UpdateAsync(client);
await _context.SaveChangesAsync();
var detail = await GetDetailAsync(client.Id);
await _cache.SetAsync(detail!);
await _cache.SyncClientCacheAsync(client.Id);
return newClient;
}

public async Task RemoveAsync(Client client)
{
await _repository.RemoveAsync(client);
await _context.SaveChangesAsync();
await _cache.RemoveAsync(client);
await _cache.RemoveClientCacheAsync(client);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
global using Microsoft.Extensions.DependencyInjection;
global using System.Data;
global using System.Linq.Expressions;
global using System.Reflection;