forked from gongxuegit/tiku-backend.net
feat: harden SaaS authentication and authorization
This commit is contained in:
@@ -9,22 +9,30 @@ internal sealed class UserConfiguration : IEntityTypeConfiguration<User>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<User> builder)
|
||||
{
|
||||
builder.ConfigureEntity("users");
|
||||
builder.ToTable("users");
|
||||
builder.HasKey(entity => entity.Id);
|
||||
builder.Property(entity => entity.Id).HasDefaultValueSql("gen_random_uuid()");
|
||||
builder.ConfigureTimestamps();
|
||||
|
||||
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
||||
builder.Property(entity => entity.Username).HasMaxLength(100);
|
||||
builder.Property(entity => entity.UserName).HasMaxLength(100);
|
||||
builder.Property(entity => entity.NormalizedUserName).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Email).HasColumnType("citext").HasMaxLength(320);
|
||||
builder.Property(entity => entity.NormalizedEmail).HasMaxLength(320);
|
||||
builder.Property(entity => entity.Phone).HasMaxLength(32);
|
||||
builder.Property(entity => entity.PhoneNumber).HasMaxLength(32);
|
||||
builder.Property(entity => entity.PasswordHash).HasMaxLength(1024);
|
||||
builder.Property(entity => entity.SecurityStamp).HasMaxLength(64);
|
||||
builder.Property(entity => entity.ConcurrencyStamp).HasMaxLength(64).IsConcurrencyToken();
|
||||
builder.Property(entity => entity.Name).HasMaxLength(200);
|
||||
builder.Property(entity => entity.AvatarUrl).HasMaxLength(2048);
|
||||
builder.Property(entity => entity.PrimaryRole).HasMaxLength(50);
|
||||
builder.Property(entity => entity.LegacyPasswordHash).HasMaxLength(512);
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.RawProfile).IsJson("{}");
|
||||
|
||||
builder.HasIndex(entity => entity.LegacyId).IsUnique();
|
||||
builder.HasIndex(entity => entity.Username).IsUnique();
|
||||
builder.HasIndex(entity => entity.Email).IsUnique();
|
||||
builder.HasIndex(entity => entity.NormalizedUserName).IsUnique();
|
||||
builder.HasIndex(entity => entity.NormalizedEmail);
|
||||
builder.HasIndex(entity => entity.Phone).IsUnique();
|
||||
}
|
||||
}
|
||||
@@ -42,8 +50,6 @@ internal sealed class UserIdentityConfiguration : IEntityTypeConfiguration<UserI
|
||||
builder.Property(entity => entity.OpenId).HasMaxLength(255);
|
||||
builder.Property(entity => entity.Phone).HasMaxLength(32);
|
||||
builder.Property(entity => entity.Email).HasColumnType("citext").HasMaxLength(320);
|
||||
builder.Property(entity => entity.SecretPayload).IsJson("{}");
|
||||
|
||||
builder.HasIndex(entity => new { entity.Provider, entity.ProviderSubject }).IsUnique();
|
||||
builder.HasOne<User>()
|
||||
.WithMany()
|
||||
|
||||
@@ -43,7 +43,6 @@ internal sealed class TenantMembershipConfiguration : IEntityTypeConfiguration<T
|
||||
|
||||
builder.Property(entity => entity.Role).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Permissions).IsJson("{}");
|
||||
builder.Property(entity => entity.LegacyRole).HasMaxLength(50);
|
||||
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.Role }).IsUnique();
|
||||
@@ -52,11 +51,6 @@ internal sealed class TenantMembershipConfiguration : IEntityTypeConfiguration<T
|
||||
.WithMany()
|
||||
.HasForeignKey(entity => entity.UserId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<TenantRoleTemplate>()
|
||||
.WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.RoleTemplateId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -13,7 +13,7 @@ using Tiku.Infrastructure.Persistence;
|
||||
namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
[DbContext(typeof(TikuDbContext))]
|
||||
[Migration("20260728014412_InitialSchema")]
|
||||
[Migration("20260728031410_InitialSchema")]
|
||||
partial class InitialSchema
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -28,6 +28,110 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "ltree");
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("FriendlyName")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("friendly_name");
|
||||
|
||||
b.Property<string>("Xml")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("xml");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_data_protection_keys");
|
||||
|
||||
b.ToTable("data_protection_keys", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("claim_type");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("claim_value");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_user_claims");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_user_claims_user_id");
|
||||
|
||||
b.ToTable("user_claims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("login_provider");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("provider_key");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("provider_display_name");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey")
|
||||
.HasName("pk_user_logins");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_user_logins_user_id");
|
||||
|
||||
b.ToTable("user_logins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("login_provider");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name")
|
||||
.HasName("pk_user_tokens");
|
||||
|
||||
b.ToTable("user_tokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Catalog.Category", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@@ -7918,11 +8022,21 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnName("id")
|
||||
.HasDefaultValueSql("gen_random_uuid()");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("access_failed_count");
|
||||
|
||||
b.Property<string>("AvatarUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)")
|
||||
.HasColumnName("avatar_url");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("concurrency_stamp");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
@@ -7934,6 +8048,14 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnType("citext")
|
||||
.HasColumnName("email");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("email_confirmed");
|
||||
|
||||
b.Property<bool>("ForcePasswordChange")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("force_password_change");
|
||||
|
||||
b.Property<DateTimeOffset?>("LastSeenAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("last_seen_at");
|
||||
@@ -7943,25 +8065,48 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("legacy_id");
|
||||
|
||||
b.Property<string>("LegacyPasswordHash")
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)")
|
||||
.HasColumnName("legacy_password_hash");
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("lockout_enabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("lockout_end");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<bool>("PasswordMigrationRequired")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("password_migration_required");
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(320)
|
||||
.HasColumnType("character varying(320)")
|
||||
.HasColumnName("normalized_email");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("normalized_user_name");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("password_hash");
|
||||
|
||||
b.Property<string>("Phone")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("phone");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("phone_number");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("phone_number_confirmed");
|
||||
|
||||
b.Property<string>("PrimaryRole")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
@@ -7978,36 +8123,50 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("score");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("security_stamp");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("two_factor_enabled");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<string>("Username")
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("username");
|
||||
.HasColumnName("user_name");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_users");
|
||||
|
||||
b.HasIndex("Email")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_users_email");
|
||||
|
||||
b.HasIndex("LegacyId")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_users_legacy_id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("email_index");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("user_name_index");
|
||||
|
||||
b.HasIndex("Phone")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_users_phone");
|
||||
|
||||
b.HasIndex("Username")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_users_username");
|
||||
|
||||
b.ToTable("users", (string)null);
|
||||
});
|
||||
|
||||
@@ -8052,12 +8211,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnType("character varying(255)")
|
||||
.HasColumnName("provider_subject");
|
||||
|
||||
b.Property<JsonElement>("SecretPayload")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("secret_payload")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<string>("UnionId")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)")
|
||||
@@ -12651,6 +12804,95 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("question_versions", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.AuthChallenge", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id")
|
||||
.HasDefaultValueSql("gen_random_uuid()");
|
||||
|
||||
b.Property<DateTimeOffset?>("ConsumedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("consumed_at");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<DateTimeOffset>("ExpiresAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expires_at");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("ip_address");
|
||||
|
||||
b.Property<string>("Provider")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("provider");
|
||||
|
||||
b.Property<string>("Purpose")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("purpose");
|
||||
|
||||
b.Property<string>("Realm")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("realm");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasColumnName("security_stamp");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("token_hash");
|
||||
|
||||
b.Property<string>("UserAgent")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("user_agent");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_auth_challenges");
|
||||
|
||||
b.HasIndex("TenantId")
|
||||
.HasDatabaseName("ix_auth_challenges_tenant_id");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_auth_challenges_token_hash");
|
||||
|
||||
b.HasIndex("UserId", "Purpose", "ExpiresAt")
|
||||
.HasDatabaseName("ix_auth_challenges_user_id_purpose_expires_at");
|
||||
|
||||
b.ToTable("auth_challenges", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("ck_auth_challenges_realm_tenant", "(realm = 'tenant' and tenant_id is not null) or (realm = 'platform' and tenant_id is null)");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@@ -12755,20 +12997,53 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnName("metadata")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<bool>("MfaSatisfied")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("mfa_satisfied");
|
||||
|
||||
b.Property<Guid?>("ParentSessionId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("parent_session_id");
|
||||
|
||||
b.Property<string>("Provider")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("provider");
|
||||
|
||||
b.Property<string>("Realm")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("realm");
|
||||
|
||||
b.Property<Guid?>("ReplacedBySessionId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("replaced_by_session_id");
|
||||
|
||||
b.Property<DateTimeOffset?>("RevokedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("revoked_at");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
b.Property<string>("RevokedReason")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("revoked_reason");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasColumnName("security_stamp");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<Guid>("TokenFamilyId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("token_family_id");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
@@ -12793,8 +13068,8 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_auth_sessions");
|
||||
|
||||
b.HasAlternateKey("TenantId", "Id")
|
||||
.HasName("ak_auth_sessions_tenant_id_id");
|
||||
b.HasIndex("TenantId")
|
||||
.HasDatabaseName("ix_auth_sessions_tenant_id");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
@@ -12804,11 +13079,17 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_auth_sessions_user_id");
|
||||
|
||||
b.HasIndex("TenantId", "UserId", "ExpiresAt")
|
||||
.HasDatabaseName("ix_auth_sessions_tenant_id_user_id_expires_at")
|
||||
b.HasIndex("TokenFamilyId", "RevokedAt")
|
||||
.HasDatabaseName("ix_auth_sessions_token_family_id_revoked_at");
|
||||
|
||||
b.HasIndex("Realm", "TenantId", "UserId", "ExpiresAt")
|
||||
.HasDatabaseName("ix_auth_sessions_realm_tenant_id_user_id_expires_at")
|
||||
.HasFilter("revoked_at is null");
|
||||
|
||||
b.ToTable("auth_sessions", (string)null);
|
||||
b.ToTable("auth_sessions", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("ck_auth_sessions_realm_tenant", "(realm = 'tenant' and tenant_id is not null) or (realm = 'platform' and tenant_id is null)");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b =>
|
||||
@@ -12934,9 +13215,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_sms_verification_codes");
|
||||
|
||||
b.HasAlternateKey("TenantId", "Id")
|
||||
.HasName("ak_sms_verification_codes_tenant_id_id");
|
||||
|
||||
b.HasIndex("TenantId", "Phone", "Purpose")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose")
|
||||
@@ -13626,22 +13904,12 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("legacy_role");
|
||||
|
||||
b.Property<JsonElement>("Permissions")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("permissions")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<string>("Role")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("role");
|
||||
|
||||
b.Property<Guid?>("RoleTemplateId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("role_template_id");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
@@ -13671,9 +13939,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_tenant_memberships_user_id");
|
||||
|
||||
b.HasIndex("TenantId", "RoleTemplateId")
|
||||
.HasDatabaseName("ix_tenant_memberships_tenant_id_role_template_id");
|
||||
|
||||
b.HasIndex("TenantId", "UserId", "Role")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_tenant_memberships_tenant_id_user_id_role");
|
||||
@@ -13681,126 +13946,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("tenant_memberships", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id")
|
||||
.HasDefaultValueSql("gen_random_uuid()");
|
||||
|
||||
b.Property<string>("BaseRole")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("base_role");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("code");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid?>("CreatedBy")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("created_by");
|
||||
|
||||
b.Property<JsonElement>("DataScope")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("data_scope")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<JsonElement>("FieldPermissions")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("field_permissions")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<bool>("IsSystem")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_system");
|
||||
|
||||
b.Property<JsonElement>("MenuPermissions")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("menu_permissions")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<JsonElement>("ModulePermissions")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("module_permissions")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<JsonElement>("Permissions")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("permissions")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("sort_order");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid?>("UpdatedBy")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("updated_by");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_tenant_role_templates");
|
||||
|
||||
b.HasAlternateKey("TenantId", "Id")
|
||||
.HasName("ak_tenant_role_templates_tenant_id_id");
|
||||
|
||||
b.HasIndex("CreatedBy")
|
||||
.HasDatabaseName("ix_tenant_role_templates_created_by");
|
||||
|
||||
b.HasIndex("UpdatedBy")
|
||||
.HasDatabaseName("ix_tenant_role_templates_updated_by");
|
||||
|
||||
b.HasIndex("TenantId", "Code")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_tenant_role_templates_tenant_id_code");
|
||||
|
||||
b.HasIndex("TenantId", "Status", "SortOrder")
|
||||
.HasDatabaseName("ix_tenant_role_templates_tenant_id_status_sort_order");
|
||||
|
||||
b.ToTable("tenant_role_templates", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSecret", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@@ -14156,6 +14301,36 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("tenant_student_notes", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_user_claims_users_user_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_user_logins_users_user_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_user_tokens_users_user_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Catalog.Category", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
@@ -17207,12 +17382,28 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasConstraintName("fk_question_versions_questions_tenant_id_question_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b =>
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.AuthChallenge", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.HasConstraintName("fk_auth_challenges_tenants_tenant_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_auth_challenges_users_user_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_auth_login_events_tenants_tenant_id");
|
||||
|
||||
@@ -17229,7 +17420,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_auth_sessions_tenants_tenant_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
@@ -17378,35 +17568,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_tenant_memberships_users_user_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Tenancy.TenantRoleTemplate", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId", "RoleTemplateId")
|
||||
.HasPrincipalKey("TenantId", "Id")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.HasConstraintName("fk_tenant_memberships_tenant_role_templates_tenant_id_role_tem~");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("CreatedBy")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("fk_tenant_role_templates_users_created_by");
|
||||
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_tenant_role_templates_tenants_tenant_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UpdatedBy")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("fk_tenant_role_templates_users_updated_by");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSecret", b =>
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Tiku.Infrastructure.Persistence;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
@@ -38,6 +38,20 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
table.UniqueConstraint("ak_backend_permissions_code", x => x.code);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "data_protection_keys",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
friendly_name = table.Column<string>(type: "text", nullable: true),
|
||||
xml = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_data_protection_keys", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "platform_backend_roles",
|
||||
columns: table => new
|
||||
@@ -137,19 +151,31 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
|
||||
username = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||
email = table.Column<string>(type: "citext", maxLength: 320, nullable: true),
|
||||
phone = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
|
||||
name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
avatar_url = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
|
||||
primary_role = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||
score = table.Column<int>(type: "integer", nullable: false),
|
||||
last_seen_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
legacy_password_hash = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: true),
|
||||
password_migration_required = table.Column<bool>(type: "boolean", nullable: false),
|
||||
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
force_password_change = table.Column<bool>(type: "boolean", nullable: false),
|
||||
raw_profile = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
user_name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||
normalized_user_name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||
email = table.Column<string>(type: "citext", maxLength: 320, nullable: true),
|
||||
normalized_email = table.Column<string>(type: "character varying(320)", maxLength: 320, nullable: true),
|
||||
email_confirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||
password_hash = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
|
||||
security_stamp = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
|
||||
concurrency_stamp = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
|
||||
phone_number = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
|
||||
phone_number_confirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||
two_factor_enabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
lockout_end = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
lockout_enabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
access_failed_count = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@@ -265,6 +291,27 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "user_claims",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
claim_type = table.Column<string>(type: "text", nullable: true),
|
||||
claim_value = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_user_claims", x => x.id);
|
||||
table.ForeignKey(
|
||||
name: "fk_user_claims_users_user_id",
|
||||
column: x => x.user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "user_identities",
|
||||
columns: table => new
|
||||
@@ -277,7 +324,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
open_id = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||
phone = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
|
||||
email = table.Column<string>(type: "citext", maxLength: 320, nullable: true),
|
||||
secret_payload = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
@@ -292,6 +338,46 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "user_logins",
|
||||
columns: table => new
|
||||
{
|
||||
login_provider = table.Column<string>(type: "text", nullable: false),
|
||||
provider_key = table.Column<string>(type: "text", nullable: false),
|
||||
provider_display_name = table.Column<string>(type: "text", nullable: true),
|
||||
user_id = table.Column<Guid>(type: "uuid", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_user_logins", x => new { x.login_provider, x.provider_key });
|
||||
table.ForeignKey(
|
||||
name: "fk_user_logins_users_user_id",
|
||||
column: x => x.user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "user_tokens",
|
||||
columns: table => new
|
||||
{
|
||||
user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
login_provider = table.Column<string>(type: "text", nullable: false),
|
||||
name = table.Column<string>(type: "text", nullable: false),
|
||||
value = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_user_tokens", x => new { x.user_id, x.login_provider, x.name });
|
||||
table.ForeignKey(
|
||||
name: "fk_user_tokens_users_user_id",
|
||||
column: x => x.user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "platform_backend_role_menus",
|
||||
columns: table => new
|
||||
@@ -403,6 +489,42 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "auth_challenges",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
realm = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
purpose = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
token_hash = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||
security_stamp = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||
provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||
expires_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
consumed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
ip_address = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||
user_agent = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_auth_challenges", x => x.id);
|
||||
table.CheckConstraint("ck_auth_challenges_realm_tenant", "(realm = 'tenant' and tenant_id is not null) or (realm = 'platform' and tenant_id is null)");
|
||||
table.ForeignKey(
|
||||
name: "fk_auth_challenges_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_auth_challenges_users_user_id",
|
||||
column: x => x.user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "auth_login_events",
|
||||
columns: table => new
|
||||
@@ -428,7 +550,7 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "fk_auth_login_events_users_user_id",
|
||||
column: x => x.user_id,
|
||||
@@ -442,22 +564,29 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
realm = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
token_family_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
parent_session_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
replaced_by_session_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
token_hash = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||
security_stamp = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||
mfa_satisfied = table.Column<bool>(type: "boolean", nullable: false),
|
||||
provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||
expires_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
revoked_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
revoked_reason = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||
ip_address = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
|
||||
user_agent = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
||||
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_auth_sessions", x => x.id);
|
||||
table.UniqueConstraint("ak_auth_sessions_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.CheckConstraint("ck_auth_sessions_realm_tenant", "(realm = 'tenant' and tenant_id is not null) or (realm = 'platform' and tenant_id is null)");
|
||||
table.ForeignKey(
|
||||
name: "fk_auth_sessions_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
@@ -1186,7 +1315,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_sms_verification_codes", x => x.id);
|
||||
table.UniqueConstraint("ak_sms_verification_codes_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.CheckConstraint("ck_sms_verification_codes_attempts", "attempts >= 0");
|
||||
table.ForeignKey(
|
||||
name: "fk_sms_verification_codes_tenants_tenant_id",
|
||||
@@ -1498,50 +1626,34 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "tenant_role_templates",
|
||||
name: "tenant_memberships",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||
name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
description = table.Column<string>(type: "text", nullable: true),
|
||||
base_role = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
role = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
permissions = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
menu_permissions = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
module_permissions = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
field_permissions = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
data_scope = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
is_system = table.Column<bool>(type: "boolean", nullable: false),
|
||||
sort_order = table.Column<int>(type: "integer", nullable: false),
|
||||
created_by = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
updated_by = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
legacy_role = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_tenant_role_templates", x => x.id);
|
||||
table.UniqueConstraint("ak_tenant_role_templates_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.PrimaryKey("pk_tenant_memberships", x => x.id);
|
||||
table.UniqueConstraint("ak_tenant_memberships_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.ForeignKey(
|
||||
name: "fk_tenant_role_templates_tenants_tenant_id",
|
||||
name: "fk_tenant_memberships_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_tenant_role_templates_users_created_by",
|
||||
column: x => x.created_by,
|
||||
name: "fk_tenant_memberships_users_user_id",
|
||||
column: x => x.user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "fk_tenant_role_templates_users_updated_by",
|
||||
column: x => x.updated_by,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
@@ -2885,45 +2997,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "tenant_memberships",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
role_template_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
role = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
permissions = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
legacy_role = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_tenant_memberships", x => x.id);
|
||||
table.UniqueConstraint("ak_tenant_memberships_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.ForeignKey(
|
||||
name: "fk_tenant_memberships_tenant_role_templates_tenant_id_role_tem~",
|
||||
columns: x => new { x.tenant_id, x.role_template_id },
|
||||
principalTable: "tenant_role_templates",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "fk_tenant_memberships_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_tenant_memberships_users_user_id",
|
||||
column: x => x.user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "content_nodes",
|
||||
columns: table => new
|
||||
@@ -6392,6 +6465,22 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
table: "audit_logs",
|
||||
columns: new[] { "tenant_id", "target_type", "target_id", "created_at" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_auth_challenges_tenant_id",
|
||||
table: "auth_challenges",
|
||||
column: "tenant_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_auth_challenges_token_hash",
|
||||
table: "auth_challenges",
|
||||
column: "token_hash",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_auth_challenges_user_id_purpose_expires_at",
|
||||
table: "auth_challenges",
|
||||
columns: new[] { "user_id", "purpose", "expires_at" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_auth_login_events_tenant_id_user_id_created_at",
|
||||
table: "auth_login_events",
|
||||
@@ -6403,11 +6492,21 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
column: "user_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_auth_sessions_tenant_id_user_id_expires_at",
|
||||
name: "ix_auth_sessions_realm_tenant_id_user_id_expires_at",
|
||||
table: "auth_sessions",
|
||||
columns: new[] { "tenant_id", "user_id", "expires_at" },
|
||||
columns: new[] { "realm", "tenant_id", "user_id", "expires_at" },
|
||||
filter: "revoked_at is null");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_auth_sessions_tenant_id",
|
||||
table: "auth_sessions",
|
||||
column: "tenant_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_auth_sessions_token_family_id_revoked_at",
|
||||
table: "auth_sessions",
|
||||
columns: new[] { "token_family_id", "revoked_at" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_auth_sessions_token_hash",
|
||||
table: "auth_sessions",
|
||||
@@ -8609,11 +8708,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
table: "tenant_invoices",
|
||||
columns: new[] { "tenant_id", "status", "due_date" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_tenant_memberships_tenant_id_role_template_id",
|
||||
table: "tenant_memberships",
|
||||
columns: new[] { "tenant_id", "role_template_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_tenant_memberships_tenant_id_user_id_role",
|
||||
table: "tenant_memberships",
|
||||
@@ -8656,27 +8750,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
table: "tenant_question_references",
|
||||
columns: new[] { "question_owner_tenant_id", "question_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_tenant_role_templates_created_by",
|
||||
table: "tenant_role_templates",
|
||||
column: "created_by");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_tenant_role_templates_tenant_id_code",
|
||||
table: "tenant_role_templates",
|
||||
columns: new[] { "tenant_id", "code" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_tenant_role_templates_tenant_id_status_sort_order",
|
||||
table: "tenant_role_templates",
|
||||
columns: new[] { "tenant_id", "status", "sort_order" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_tenant_role_templates_updated_by",
|
||||
table: "tenant_role_templates",
|
||||
column: "updated_by");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_tenant_secrets_tenant_id_purpose_provider_secret_key",
|
||||
table: "tenant_secrets",
|
||||
@@ -8850,6 +8923,11 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
table: "user_badges",
|
||||
column: "user_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_user_claims_user_id",
|
||||
table: "user_claims",
|
||||
column: "user_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_user_identities_provider_provider_subject",
|
||||
table: "user_identities",
|
||||
@@ -8861,6 +8939,11 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
table: "user_identities",
|
||||
column: "user_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_user_logins_user_id",
|
||||
table: "user_logins",
|
||||
column: "user_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_user_notifications_created_by",
|
||||
table: "user_notifications",
|
||||
@@ -8942,10 +9025,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
column: "user_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_users_email",
|
||||
name: "email_index",
|
||||
table: "users",
|
||||
column: "email",
|
||||
unique: true);
|
||||
column: "normalized_email");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_users_legacy_id",
|
||||
@@ -8960,9 +9042,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_users_username",
|
||||
name: "user_name_index",
|
||||
table: "users",
|
||||
column: "username",
|
||||
column: "normalized_user_name",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
@@ -9347,6 +9429,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
migrationBuilder.DropTable(
|
||||
name: "app_assets");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "auth_challenges");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "auth_login_events");
|
||||
|
||||
@@ -9395,6 +9480,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
migrationBuilder.DropTable(
|
||||
name: "dashboard_daily_stats");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "data_protection_keys");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "entitlements");
|
||||
|
||||
@@ -9578,15 +9666,24 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
migrationBuilder.DropTable(
|
||||
name: "user_badges");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "user_claims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "user_identities");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "user_logins");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "user_notifications");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "user_score_events");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "user_tokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "user_word_favorites");
|
||||
|
||||
@@ -9662,9 +9759,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
migrationBuilder.DropTable(
|
||||
name: "tenant_backend_roles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "tenant_role_templates");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "tenant_classes");
|
||||
|
||||
@@ -25,6 +25,110 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "ltree");
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("FriendlyName")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("friendly_name");
|
||||
|
||||
b.Property<string>("Xml")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("xml");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_data_protection_keys");
|
||||
|
||||
b.ToTable("data_protection_keys", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("claim_type");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("claim_value");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_user_claims");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_user_claims_user_id");
|
||||
|
||||
b.ToTable("user_claims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("login_provider");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("provider_key");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("provider_display_name");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey")
|
||||
.HasName("pk_user_logins");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_user_logins_user_id");
|
||||
|
||||
b.ToTable("user_logins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("login_provider");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name")
|
||||
.HasName("pk_user_tokens");
|
||||
|
||||
b.ToTable("user_tokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Catalog.Category", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@@ -7915,11 +8019,21 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnName("id")
|
||||
.HasDefaultValueSql("gen_random_uuid()");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("access_failed_count");
|
||||
|
||||
b.Property<string>("AvatarUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)")
|
||||
.HasColumnName("avatar_url");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("concurrency_stamp");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
@@ -7931,6 +8045,14 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnType("citext")
|
||||
.HasColumnName("email");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("email_confirmed");
|
||||
|
||||
b.Property<bool>("ForcePasswordChange")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("force_password_change");
|
||||
|
||||
b.Property<DateTimeOffset?>("LastSeenAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("last_seen_at");
|
||||
@@ -7940,25 +8062,48 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("legacy_id");
|
||||
|
||||
b.Property<string>("LegacyPasswordHash")
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)")
|
||||
.HasColumnName("legacy_password_hash");
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("lockout_enabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("lockout_end");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<bool>("PasswordMigrationRequired")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("password_migration_required");
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(320)
|
||||
.HasColumnType("character varying(320)")
|
||||
.HasColumnName("normalized_email");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("normalized_user_name");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("password_hash");
|
||||
|
||||
b.Property<string>("Phone")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("phone");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("phone_number");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("phone_number_confirmed");
|
||||
|
||||
b.Property<string>("PrimaryRole")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
@@ -7975,36 +8120,50 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("score");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("security_stamp");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("two_factor_enabled");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<string>("Username")
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("username");
|
||||
.HasColumnName("user_name");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_users");
|
||||
|
||||
b.HasIndex("Email")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_users_email");
|
||||
|
||||
b.HasIndex("LegacyId")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_users_legacy_id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("email_index");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("user_name_index");
|
||||
|
||||
b.HasIndex("Phone")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_users_phone");
|
||||
|
||||
b.HasIndex("Username")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_users_username");
|
||||
|
||||
b.ToTable("users", (string)null);
|
||||
});
|
||||
|
||||
@@ -8049,12 +8208,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnType("character varying(255)")
|
||||
.HasColumnName("provider_subject");
|
||||
|
||||
b.Property<JsonElement>("SecretPayload")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("secret_payload")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<string>("UnionId")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)")
|
||||
@@ -12648,6 +12801,95 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("question_versions", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.AuthChallenge", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id")
|
||||
.HasDefaultValueSql("gen_random_uuid()");
|
||||
|
||||
b.Property<DateTimeOffset?>("ConsumedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("consumed_at");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<DateTimeOffset>("ExpiresAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expires_at");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("ip_address");
|
||||
|
||||
b.Property<string>("Provider")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("provider");
|
||||
|
||||
b.Property<string>("Purpose")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("purpose");
|
||||
|
||||
b.Property<string>("Realm")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("realm");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasColumnName("security_stamp");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("token_hash");
|
||||
|
||||
b.Property<string>("UserAgent")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("user_agent");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_auth_challenges");
|
||||
|
||||
b.HasIndex("TenantId")
|
||||
.HasDatabaseName("ix_auth_challenges_tenant_id");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_auth_challenges_token_hash");
|
||||
|
||||
b.HasIndex("UserId", "Purpose", "ExpiresAt")
|
||||
.HasDatabaseName("ix_auth_challenges_user_id_purpose_expires_at");
|
||||
|
||||
b.ToTable("auth_challenges", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("ck_auth_challenges_realm_tenant", "(realm = 'tenant' and tenant_id is not null) or (realm = 'platform' and tenant_id is null)");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@@ -12752,20 +12994,53 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnName("metadata")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<bool>("MfaSatisfied")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("mfa_satisfied");
|
||||
|
||||
b.Property<Guid?>("ParentSessionId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("parent_session_id");
|
||||
|
||||
b.Property<string>("Provider")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("provider");
|
||||
|
||||
b.Property<string>("Realm")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("realm");
|
||||
|
||||
b.Property<Guid?>("ReplacedBySessionId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("replaced_by_session_id");
|
||||
|
||||
b.Property<DateTimeOffset?>("RevokedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("revoked_at");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
b.Property<string>("RevokedReason")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("revoked_reason");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasColumnName("security_stamp");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<Guid>("TokenFamilyId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("token_family_id");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
@@ -12790,8 +13065,8 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_auth_sessions");
|
||||
|
||||
b.HasAlternateKey("TenantId", "Id")
|
||||
.HasName("ak_auth_sessions_tenant_id_id");
|
||||
b.HasIndex("TenantId")
|
||||
.HasDatabaseName("ix_auth_sessions_tenant_id");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
@@ -12801,11 +13076,17 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_auth_sessions_user_id");
|
||||
|
||||
b.HasIndex("TenantId", "UserId", "ExpiresAt")
|
||||
.HasDatabaseName("ix_auth_sessions_tenant_id_user_id_expires_at")
|
||||
b.HasIndex("TokenFamilyId", "RevokedAt")
|
||||
.HasDatabaseName("ix_auth_sessions_token_family_id_revoked_at");
|
||||
|
||||
b.HasIndex("Realm", "TenantId", "UserId", "ExpiresAt")
|
||||
.HasDatabaseName("ix_auth_sessions_realm_tenant_id_user_id_expires_at")
|
||||
.HasFilter("revoked_at is null");
|
||||
|
||||
b.ToTable("auth_sessions", (string)null);
|
||||
b.ToTable("auth_sessions", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("ck_auth_sessions_realm_tenant", "(realm = 'tenant' and tenant_id is not null) or (realm = 'platform' and tenant_id is null)");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b =>
|
||||
@@ -12931,9 +13212,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_sms_verification_codes");
|
||||
|
||||
b.HasAlternateKey("TenantId", "Id")
|
||||
.HasName("ak_sms_verification_codes_tenant_id_id");
|
||||
|
||||
b.HasIndex("TenantId", "Phone", "Purpose")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose")
|
||||
@@ -13623,22 +13901,12 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("legacy_role");
|
||||
|
||||
b.Property<JsonElement>("Permissions")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("permissions")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<string>("Role")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("role");
|
||||
|
||||
b.Property<Guid?>("RoleTemplateId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("role_template_id");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
@@ -13668,9 +13936,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_tenant_memberships_user_id");
|
||||
|
||||
b.HasIndex("TenantId", "RoleTemplateId")
|
||||
.HasDatabaseName("ix_tenant_memberships_tenant_id_role_template_id");
|
||||
|
||||
b.HasIndex("TenantId", "UserId", "Role")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_tenant_memberships_tenant_id_user_id_role");
|
||||
@@ -13678,126 +13943,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("tenant_memberships", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id")
|
||||
.HasDefaultValueSql("gen_random_uuid()");
|
||||
|
||||
b.Property<string>("BaseRole")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("base_role");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("code");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid?>("CreatedBy")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("created_by");
|
||||
|
||||
b.Property<JsonElement>("DataScope")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("data_scope")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<JsonElement>("FieldPermissions")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("field_permissions")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<bool>("IsSystem")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_system");
|
||||
|
||||
b.Property<JsonElement>("MenuPermissions")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("menu_permissions")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<JsonElement>("ModulePermissions")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("module_permissions")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<JsonElement>("Permissions")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("permissions")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("sort_order");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid?>("UpdatedBy")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("updated_by");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_tenant_role_templates");
|
||||
|
||||
b.HasAlternateKey("TenantId", "Id")
|
||||
.HasName("ak_tenant_role_templates_tenant_id_id");
|
||||
|
||||
b.HasIndex("CreatedBy")
|
||||
.HasDatabaseName("ix_tenant_role_templates_created_by");
|
||||
|
||||
b.HasIndex("UpdatedBy")
|
||||
.HasDatabaseName("ix_tenant_role_templates_updated_by");
|
||||
|
||||
b.HasIndex("TenantId", "Code")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_tenant_role_templates_tenant_id_code");
|
||||
|
||||
b.HasIndex("TenantId", "Status", "SortOrder")
|
||||
.HasDatabaseName("ix_tenant_role_templates_tenant_id_status_sort_order");
|
||||
|
||||
b.ToTable("tenant_role_templates", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSecret", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@@ -14153,6 +14298,36 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("tenant_student_notes", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_user_claims_users_user_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_user_logins_users_user_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_user_tokens_users_user_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Catalog.Category", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
@@ -17204,12 +17379,28 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasConstraintName("fk_question_versions_questions_tenant_id_question_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b =>
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.AuthChallenge", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.HasConstraintName("fk_auth_challenges_tenants_tenant_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_auth_challenges_users_user_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_auth_login_events_tenants_tenant_id");
|
||||
|
||||
@@ -17226,7 +17417,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_auth_sessions_tenants_tenant_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
@@ -17375,35 +17565,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_tenant_memberships_users_user_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Tenancy.TenantRoleTemplate", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId", "RoleTemplateId")
|
||||
.HasPrincipalKey("TenantId", "Id")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.HasConstraintName("fk_tenant_memberships_tenant_role_templates_tenant_id_role_tem~");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("CreatedBy")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("fk_tenant_role_templates_users_created_by");
|
||||
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_tenant_role_templates_tenants_tenant_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UpdatedBy")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("fk_tenant_role_templates_users_updated_by");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSecret", b =>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.Reflection;
|
||||
using Tiku.Application.Security;
|
||||
using Tiku.Domain.Catalog;
|
||||
@@ -18,7 +21,7 @@ namespace Tiku.Infrastructure.Persistence;
|
||||
|
||||
public sealed class TikuDbContext(
|
||||
DbContextOptions<TikuDbContext> options,
|
||||
ITenantContext tenantContext) : DbContext(options)
|
||||
ITenantContext tenantContext) : IdentityUserContext<User, Guid>(options), IDataProtectionKeyContext
|
||||
{
|
||||
public TikuDbContext(DbContextOptions<TikuDbContext> options)
|
||||
: this(options, CreateToolingTenantContext())
|
||||
@@ -37,7 +40,8 @@ public sealed class TikuDbContext(
|
||||
return context;
|
||||
}
|
||||
public DbSet<Tenant> Tenants => Set<Tenant>();
|
||||
public DbSet<User> Users => Set<User>();
|
||||
public new DbSet<User> Users => Set<User>();
|
||||
public DbSet<DataProtectionKey> DataProtectionKeys => Set<DataProtectionKey>();
|
||||
public DbSet<UserIdentity> UserIdentities => Set<UserIdentity>();
|
||||
public DbSet<TenantMembership> TenantMemberships => Set<TenantMembership>();
|
||||
public DbSet<TenantDomain> TenantDomains => Set<TenantDomain>();
|
||||
@@ -49,8 +53,8 @@ public sealed class TikuDbContext(
|
||||
public DbSet<SmsVerificationCode> SmsVerificationCodes => Set<SmsVerificationCode>();
|
||||
public DbSet<AuthLoginEvent> AuthLoginEvents => Set<AuthLoginEvent>();
|
||||
public DbSet<AuthSession> AuthSessions => Set<AuthSession>();
|
||||
public DbSet<AuthChallenge> AuthChallenges => Set<AuthChallenge>();
|
||||
public DbSet<SmsSendRateLimit> SmsSendRateLimits => Set<SmsSendRateLimit>();
|
||||
public DbSet<TenantRoleTemplate> TenantRoleTemplates => Set<TenantRoleTemplate>();
|
||||
public DbSet<TenantClass> TenantClasses => Set<TenantClass>();
|
||||
public DbSet<TenantClassMember> TenantClassMembers => Set<TenantClassMember>();
|
||||
public DbSet<TenantStudentNote> TenantStudentNotes => Set<TenantStudentNote>();
|
||||
@@ -186,9 +190,14 @@ public sealed class TikuDbContext(
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
modelBuilder.Entity<IdentityUserClaim<Guid>>().ToTable("user_claims");
|
||||
modelBuilder.Entity<IdentityUserLogin<Guid>>().ToTable("user_logins");
|
||||
modelBuilder.Entity<IdentityUserToken<Guid>>().ToTable("user_tokens");
|
||||
modelBuilder.HasPostgresExtension("citext");
|
||||
modelBuilder.HasPostgresExtension("ltree");
|
||||
modelBuilder.ApplyConfigurationsFromAssembly(typeof(TikuDbContext).Assembly);
|
||||
modelBuilder.Entity<DataProtectionKey>().ToTable("data_protection_keys");
|
||||
ApplyTenantQueryFilters(modelBuilder);
|
||||
ValidateTenantModel(modelBuilder);
|
||||
modelBuilder.UseSnakeCaseIdentifiers();
|
||||
@@ -298,6 +307,15 @@ public sealed class TikuDbContext(
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
|
||||
foreach (var entry in ChangeTracker.Entries<User>().Where(entry => entry.State == EntityState.Modified))
|
||||
{
|
||||
if (entry.Property(user => user.Status).IsModified ||
|
||||
entry.Property(user => user.PasswordHash).IsModified)
|
||||
{
|
||||
entry.Entity.SecurityStamp = Guid.NewGuid().ToString("N");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entry in ChangeTracker.Entries<IHasTimestamps>())
|
||||
{
|
||||
if (entry.State == EntityState.Added)
|
||||
|
||||
Reference in New Issue
Block a user