-
Notifications
You must be signed in to change notification settings - Fork 159
/
AuthPermissionsDbContext.cs
151 lines (133 loc) · 6.29 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
// Copyright (c) 2021 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>
/// 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>
/// 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 = this.Database.ProviderName;
_customConfiguration = customConfiguration;
}
/// <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>
/// 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();
}
}
}