aboutsummaryrefslogtreecommitdiff
path: root/VNLib.Plugins.Essentials.Accounts.Registration/src/TokenRevocation/RevokedTokenStore.cs
blob: ccc7b374ec4d3c7ee83f5c495d910d7077801d3b (plain)
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
using System.Collections;

using Microsoft.EntityFrameworkCore;

using VNLib.Utils;

namespace VNLib.Plugins.Essentials.Accounts.Registration.TokenRevocation
{
    internal class RevokedTokenStore
    {
        private readonly DbContextOptions Options;

        public RevokedTokenStore(DbContextOptions options)
        {
            Options = options;
        }

        public async Task<bool> IsRevokedAsync(string token, CancellationToken cancellation)
        {
            await using RevocationContext context = new (Options);
            await context.OpenTransactionAsync(cancellation);

            //Select any that match tokens
            bool any = await (from t in context.RevokedRegistrationTokens
                              where t.Token == token
                              select t).AnyAsync(cancellation);

            await context.CommitTransactionAsync(cancellation);
            return any;
        }

        public async Task RevokeAsync(string token, CancellationToken cancellation)
        {
            await using RevocationContext context = new (Options);
            await context.OpenTransactionAsync(cancellation);

            //Add to table
            context.RevokedRegistrationTokens.Add(new RevokedToken()
            {
                Created = DateTime.UtcNow,
                Token = token
            });

            //Save changes and commit transaction
            await context.SaveChangesAsync(cancellation);
            await context.CommitTransactionAsync(cancellation);
        }

        /// <summary>
        /// Removes expired records from the store
        /// </summary>
        /// <param name="validFor">The time a token is valid for</param>
        /// <param name="cancellation">A token that cancels the async operation</param>
        /// <returns>The number of records evicted from the store</returns>
        public async Task<ERRNO> CleanTableAsync(TimeSpan validFor, CancellationToken cancellation)
        {
            DateTime expiredBefore = DateTime.UtcNow.Subtract(validFor);

            await using RevocationContext context = new (Options);
            await context.OpenTransactionAsync(cancellation);

            //Select any that match tokens
            RevokedToken[] expired = await context.RevokedRegistrationTokens.Where(t => t.Created < expiredBefore)
                .Select(static t => t)
                .ToArrayAsync(cancellation);


            context.RevokedRegistrationTokens.RemoveRange(expired);

            ERRNO count =await context.SaveChangesAsync(cancellation);
            
            await context.CommitTransactionAsync(cancellation);

            return count;
        }
    }
}