-
Notifications
You must be signed in to change notification settings - Fork 157
/
AuthPermissionsDbContext.cs
167 lines (142 loc) · 6.7 KB
/
AuthPermissionsDbContext.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Copyright (c) 2023 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/
// Licensed under MIT license. See License.txt in the project root for license information.
using AuthPermissions.BaseCode.DataLayer.Classes;
using AuthPermissions.BaseCode.DataLayer.Classes.SupportTypes;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
namespace AuthPermissions.BaseCode.DataLayer.EfCode
{
/// <summary>
/// This forms the AuthP's EF Core database
/// </summary>
public class AuthPermissionsDbContext : DbContext
{
private readonly ICustomConfiguration _customConfiguration;
/// <summary>
/// ctor
/// </summary>
/// <param name="options"></param>
/// <param name="eventSetups">OPTIONAL: If provided, then a method will be run within the ctor</param>
/// <param name="customConfiguration">OPTIONAL: This allows to provide a custom configuration to the DbContext</param>
public AuthPermissionsDbContext(DbContextOptions<AuthPermissionsDbContext> options,
IEnumerable<IDatabaseStateChangeEvent> eventSetups = null,
ICustomConfiguration customConfiguration = null)
: base(options)
{
foreach (var eventSetup in eventSetups ?? Array.Empty<IDatabaseStateChangeEvent>())
{
eventSetup.RegisterEventHandlers(this);
}
ProviderName = Database.ProviderName;
_customConfiguration = customConfiguration;
}
/// <summary>
/// This overcomes the exception if the class used in the tests which uses the <see cref="IModelCacheKeyFactory"/>
/// to allow testing of an DbContext that works with SqlServer and PostgreSQL
/// </summary>
public string ProviderName { get; }
/// <summary>
/// The list of AuthUsers defining what roles and tenant that user has
/// </summary>
public DbSet<AuthUser> AuthUsers { get; set; }
/// <summary>
/// A list of all the AuthP's Roles, each with the permissions in each Role
/// </summary>
public DbSet<RoleToPermissions> RoleToPermissions { get; set; }
/// <summary>
/// When using AuthP's multi-tenant feature these define each tenant and the DataKey to access data in that tenant
/// </summary>
public DbSet<Tenant> Tenants { get; set; }
/// <summary>
/// This links AuthP's Roles to a AuthUser
/// </summary>
public DbSet<UserToRole> UserToRoles { get; set; }
/// <summary>
/// If you use AuthP's JWT refresh token, then the tokens are held in this entity
/// </summary>
public DbSet<RefreshToken> RefreshTokens { get; set; }
/// <summary>
/// This holds the backup set of <see cref="ShardingEntry"/>'s held in the FileStore cache
/// </summary>
public DbSet<ShardingEntry> ShardingEntryBackup { get; set; }
/// <summary>
/// Set up AuthP's setup
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("authp");
//Add concurrency token to every entity
foreach (IMutableEntityType entityType in modelBuilder.Model.GetEntityTypes())
{
if (Database.IsSqlServer())
{
entityType.AddProperty("ConcurrencyToken", typeof(byte[]))
.SetColumnType("ROWVERSION");
entityType.FindProperty("ConcurrencyToken")
.ValueGenerated = ValueGenerated.OnAddOrUpdate;
entityType.FindProperty("ConcurrencyToken")
.IsConcurrencyToken = true;
}
else if (Database.IsNpgsql())
{
//see https://www.npgsql.org/efcore/modeling/concurrency.html
//and https://github.com/npgsql/efcore.pg/issues/19#issuecomment-253346255
entityType.AddProperty("xmin", typeof(uint))
.SetColumnType("xid");
entityType.FindProperty("xmin")
.ValueGenerated = ValueGenerated.OnAddOrUpdate;
entityType.FindProperty("xmin")
.IsConcurrencyToken = true;
}
//NOTE: Sqlite doesn't support concurrency support, but if needed it can be added
//see https://www.bricelam.net/2020/08/07/sqlite-and-efcore-concurrency-tokens.html
}
//This allows a developer to add a custom configuration to this DbContext
//Typical use is to set up the concurrency tokens parts when using a custom database type
_customConfiguration?.ApplyCustomConfiguration(modelBuilder);
modelBuilder.Entity<AuthUser>()
.HasIndex(x => x.Email)
.IsUnique();
modelBuilder.Entity<AuthUser>()
.HasIndex(x => x.UserName)
.IsUnique();
modelBuilder.Entity<AuthUser>()
.HasMany(x => x.UserRoles)
.WithOne()
.HasForeignKey(x => x.UserId);
modelBuilder.Entity<RoleToPermissions>()
.HasIndex(x => x.RoleType);
modelBuilder.Entity<UserToRole>()
.HasKey(x => new { x.UserId, x.RoleName });
modelBuilder.Entity<Tenant>().HasKey(x => x.TenantId);
modelBuilder.Entity<Tenant>()
.HasIndex(x => x.TenantFullName)
.IsUnique();
modelBuilder.Entity<Tenant>()
.Property(x => x.ParentDataKey)
.IsUnicode(false);
modelBuilder.Entity<Tenant>()
.HasIndex(x => x.ParentDataKey);
modelBuilder.Entity<Tenant>()
.HasMany(x => x.TenantRoles)
.WithMany(x => x.Tenants);
modelBuilder.Entity<RefreshToken>()
.Property(x => x.TokenValue)
.IsUnicode(false)
.HasMaxLength(AuthDbConstants.RefreshTokenValueSize)
.IsRequired();
modelBuilder.Entity<RefreshToken>()
.HasKey(x => x.TokenValue);
modelBuilder.Entity<RefreshToken>()
.HasIndex(x => x.AddedDateUtc)
.IsUnique();
modelBuilder.Entity<ShardingEntry>()
.HasKey(x => x.Name);
modelBuilder.Entity<ShardingEntry>()
.HasIndex(x => x.Name)
.IsUnique();
}
}
}