-
Notifications
You must be signed in to change notification settings - Fork 37
/
DatabaseJsonWebKeyStore.cs
132 lines (114 loc) · 10.8 KB
/
DatabaseJsonWebKeyStore.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NetDevPack.Security.Jwt.Core;
using NetDevPack.Security.Jwt.Core.Interfaces;
using NetDevPack.Security.Jwt.Core.Model;
namespace NetDevPack.Security.Jwt.Store.EntityFrameworkCore
{
internal class DatabaseJsonWebKeyStore<TContext> : IJsonWebKeyStore
where TContext : DbContext, ISecurityKeyContext
{
private readonly TContext _context;
private readonly IOptions<JwtOptions> _options;
private readonly IMemoryCache _memoryCache;
private readonly ILogger<DatabaseJsonWebKeyStore<TContext>> _logger;
public DatabaseJsonWebKeyStore(TContext context, ILogger<DatabaseJsonWebKeyStore<TContext>> logger, IOptions<JwtOptions> options, IMemoryCache memoryCache)
{
_context = context;
_options = options;
_memoryCache = memoryCache;
_logger = logger;
}
public async Task Store(KeyMaterial securityParamteres)
{
await _context.SecurityKeys.AddAsync(securityParamteres);
_logger.LogInformation($"Saving new SecurityKeyWithPrivate {securityParamteres.Id}", typeof(TContext).Name);
await _context.SaveChangesAsync();
ClearCache();
}
public async Task<KeyMaterial> GetCurrent()
{
if (!_memoryCache.TryGetValue(JwkContants.CurrentJwkCache, out KeyMaterial credentials))
{
#if NET5_0_OR_GREATER
credentials = await _context.SecurityKeys.Where(X => X.IsRevoked == false).OrderByDescending(d => d.CreationDate).AsNoTrackingWithIdentityResolution().FirstOrDefaultAsync();
#else
credentials = await _context.SecurityKeys.Where(X => X.IsRevoked == false).OrderByDescending(d => d.CreationDate).AsNoTracking().FirstOrDefaultAsync();
#endif
// Set cache options.
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Keep in cache for this time, reset time if accessed.
.SetSlidingExpiration(_options.Value.CacheTime);
if (credentials != null)
_memoryCache.Set(JwkContants.CurrentJwkCache, credentials, cacheEntryOptions);
return credentials;
}
// Put logger in a local such that `this` isn't captured.
#if NET5_0_OR_GREATER
return await _context.SecurityKeys.OrderByDescending(d => d.CreationDate).AsNoTrackingWithIdentityResolution().FirstOrDefaultAsync();
#else
return await _context.SecurityKeys.OrderByDescending(d => d.CreationDate).AsNoTracking().FirstOrDefaultAsync();
#endif
}
public async Task<ReadOnlyCollection<KeyMaterial>> GetLastKeys(int quantity = 5)
{
if (!_memoryCache.TryGetValue(JwkContants.JwksCache, out ReadOnlyCollection<KeyMaterial> keys))
{
#if NET5_0_OR_GREATER
keys = _context.SecurityKeys.OrderByDescending(d => d.CreationDate).Take(quantity).AsNoTrackingWithIdentityResolution().ToList().AsReadOnly();
#else
keys = _context.SecurityKeys.OrderByDescending(d => d.CreationDate).Take(quantity).AsNoTracking().ToList().AsReadOnly();
#endif
// Set cache options.
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Keep in cache for this time, reset time if accessed.
.SetSlidingExpiration(_options.Value.CacheTime);
if (keys.Any())
_memoryCache.Set(JwkContants.JwksCache, keys, cacheEntryOptions);
return keys;
}
#if NET5_0_OR_GREATER
var query = await _context.SecurityKeys.OrderByDescending(d => d.CreationDate).Take(quantity).AsNoTrackingWithIdentityResolution().ToListAsync();
#else
var query = await _context.SecurityKeys.OrderByDescending(d => d.CreationDate).Take(quantity).AsNoTracking().ToListAsync();
#endif
return query.AsReadOnly();
}
public Task<KeyMaterial> Get(string keyId)
{
return _context.SecurityKeys.FirstOrDefaultAsync(f => f.KeyId == keyId);
}
public async Task Clear()
{
foreach (var securityKeyWithPrivate in _context.SecurityKeys)
{
_context.SecurityKeys.Remove(securityKeyWithPrivate);
}
await _context.SaveChangesAsync();
ClearCache();
}
public async Task Revoke(KeyMaterial securityKeyWithPrivate, string reason = null)
{
if (securityKeyWithPrivate == null)
return;
securityKeyWithPrivate.Revoke();
_context.Attach(securityKeyWithPrivate);
_context.SecurityKeys.Update(securityKeyWithPrivate);
await _context.SaveChangesAsync();
ClearCache();
}
private void ClearCache()
{
_memoryCache.Remove(JwkContants.JwksCache);
_memoryCache.Remove(JwkContants.CurrentJwkCache);
}
}
}