forked from xiongyuxing/tiku-backend.net
feat: harden SaaS authentication and authorization
This commit is contained in:
@@ -51,7 +51,6 @@ internal sealed class SmsVerificationCodeConfiguration : IEntityTypeConfiguratio
|
||||
public void Configure(EntityTypeBuilder<SmsVerificationCode> builder)
|
||||
{
|
||||
builder.ConfigureEntity("sms_verification_codes");
|
||||
builder.HasAlternateKey(entity => new { entity.TenantId, entity.Id });
|
||||
builder.Property(entity => entity.Phone).HasMaxLength(32);
|
||||
builder.Property(entity => entity.Purpose).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.CodeHash).HasMaxLength(256);
|
||||
@@ -96,7 +95,7 @@ internal sealed class AuthLoginEventConfiguration : IEntityTypeConfiguration<Aut
|
||||
|
||||
builder.HasOne<Tenant>().WithMany()
|
||||
.HasForeignKey(entity => entity.TenantId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
builder.HasOne<User>().WithMany()
|
||||
.HasForeignKey(entity => entity.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
@@ -107,18 +106,30 @@ internal sealed class AuthSessionConfiguration : IEntityTypeConfiguration<AuthSe
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<AuthSession> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("auth_sessions");
|
||||
builder.ConfigureEntity("auth_sessions");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.Realm).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.TokenHash).HasMaxLength(256);
|
||||
builder.Property(entity => entity.SecurityStamp).HasMaxLength(128);
|
||||
builder.Property(entity => entity.Provider).HasMaxLength(50);
|
||||
builder.Property(entity => entity.RevokedReason).HasMaxLength(100);
|
||||
builder.Property(entity => entity.IpAddress).HasMaxLength(64);
|
||||
builder.Property(entity => entity.UserAgent).HasMaxLength(1000);
|
||||
builder.Property(entity => entity.Metadata).IsJson("{}");
|
||||
builder.HasIndex(entity => entity.TokenHash)
|
||||
.IsUnique()
|
||||
.HasAnnotation("Tiku:GlobalUnique", true);
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.ExpiresAt })
|
||||
builder.HasIndex(entity => new { entity.Realm, entity.TenantId, entity.UserId, entity.ExpiresAt })
|
||||
.HasFilter("revoked_at is null");
|
||||
builder.HasIndex(entity => new { entity.TokenFamilyId, entity.RevokedAt });
|
||||
|
||||
builder.ToTable(table => table.HasCheckConstraint(
|
||||
"ck_auth_sessions_realm_tenant",
|
||||
"(realm = 'tenant' and tenant_id is not null) or (realm = 'platform' and tenant_id is null)"));
|
||||
|
||||
builder.HasOne<Tenant>().WithMany()
|
||||
.HasForeignKey(entity => entity.TenantId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasOne<User>().WithMany()
|
||||
.HasForeignKey(entity => entity.UserId)
|
||||
@@ -126,6 +137,29 @@ internal sealed class AuthSessionConfiguration : IEntityTypeConfiguration<AuthSe
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class AuthChallengeConfiguration : IEntityTypeConfiguration<AuthChallenge>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<AuthChallenge> builder)
|
||||
{
|
||||
builder.ConfigureEntity("auth_challenges");
|
||||
builder.Property(entity => entity.Realm).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Purpose).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.TokenHash).HasMaxLength(64);
|
||||
builder.Property(entity => entity.SecurityStamp).HasMaxLength(128);
|
||||
builder.Property(entity => entity.Provider).HasMaxLength(50);
|
||||
builder.Property(entity => entity.IpAddress).HasMaxLength(100);
|
||||
builder.Property(entity => entity.UserAgent).HasMaxLength(1024);
|
||||
builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()");
|
||||
builder.HasIndex(entity => entity.TokenHash).IsUnique();
|
||||
builder.HasIndex(entity => new { entity.UserId, entity.Purpose, entity.ExpiresAt });
|
||||
builder.ToTable(table => table.HasCheckConstraint(
|
||||
"ck_auth_challenges_realm_tenant",
|
||||
"(realm = 'tenant' and tenant_id is not null) or (realm = 'platform' and tenant_id is null)"));
|
||||
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.UserId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<Tenant>().WithMany().HasForeignKey(entity => entity.TenantId).OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SmsSendRateLimitConfiguration : IEntityTypeConfiguration<SmsSendRateLimit>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SmsSendRateLimit> builder)
|
||||
@@ -154,32 +188,6 @@ internal sealed class SmsSendRateLimitConfiguration : IEntityTypeConfiguration<S
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantRoleTemplateConfiguration : IEntityTypeConfiguration<TenantRoleTemplate>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantRoleTemplate> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("tenant_role_templates");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.Code).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Name).HasMaxLength(200);
|
||||
builder.Property(entity => entity.BaseRole).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Permissions).IsJson("{}");
|
||||
builder.Property(entity => entity.MenuPermissions).IsJson("{}");
|
||||
builder.Property(entity => entity.ModulePermissions).IsJson("{}");
|
||||
builder.Property(entity => entity.FieldPermissions).IsJson("{}");
|
||||
builder.Property(entity => entity.DataScope).IsJson("{}");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.Code }).IsUnique();
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.SortOrder });
|
||||
builder.HasOne<User>().WithMany()
|
||||
.HasForeignKey(entity => entity.CreatedBy)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
builder.HasOne<User>().WithMany()
|
||||
.HasForeignKey(entity => entity.UpdatedBy)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantClassConfiguration : IEntityTypeConfiguration<TenantClass>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantClass> builder)
|
||||
|
||||
Reference in New Issue
Block a user