< Summary - Heimdall API

Line coverage
97%
Covered lines: 952
Uncovered lines: 26
Coverable lines: 978
Total lines: 1093
Line coverage: 97.3%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage
66%
Covered methods: 2
Fully covered methods: 2
Total methods: 3
Method coverage: 66.6%
Full method coverage: 66.6%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: Up(...)100%11100%
File 1: Down(...)100%210%
File 2: BuildTargetModel(...)100%11100%

File(s)

/home/runner/work/heimdall-api/heimdall-api/src/Infrastructure/ArturRios.Heimdall.Data/Migrations/20260814160156_AddBruteForceGuardsAndEmailUniqueness.cs

#LineLine coverage
 1using System;
 2using Microsoft.EntityFrameworkCore.Migrations;
 3
 4#nullable disable
 5
 6namespace ArturRios.Heimdall.Data.Migrations
 7{
 8    /// <summary>
 9    ///     Adds the per-account brute-force counters (FR-AU-09, FR-2F-13) and the TOTP replay guard
 10    ///     (FR-2F-09), and turns the two email-uniqueness rules a unique index can express into
 11    ///     actual unique indexes (FR-PE-09, FR-GO-07).
 12    /// </summary>
 13    /// <remarks>
 14    ///     The indexes are raw SQL because EF Core models neither an index over an expression
 15    ///     (LOWER(email)) nor one filtered on a role set, and both properties matter: the application
 16    ///     compares addresses case-insensitively, so an index over the raw column would enforce a
 17    ///     different rule than the handlers apply, and an administrator's address only has to be
 18    ///     unique among live administrators.
 19    /// </remarks>
 20    public partial class AddBruteForceGuardsAndEmailUniqueness : Migration
 21    {
 22        /// <inheritdoc />
 23        protected override void Up(MigrationBuilder migrationBuilder)
 24        {
 425            migrationBuilder.AddColumn<int>(
 426                name: "failed_attempts",
 427                schema: "heimdall",
 428                table: "two_factor_email_code",
 429                type: "integer",
 430                nullable: false,
 431                defaultValue: 0);
 32
 433            migrationBuilder.AddColumn<long>(
 434                name: "last_totp_time_step_used",
 435                schema: "heimdall",
 436                table: "two_factor_auth",
 437                type: "bigint",
 438                nullable: true);
 39
 440            migrationBuilder.AddColumn<int>(
 441                name: "failed_login_attempts",
 442                schema: "heimdall",
 443                table: "person",
 444                type: "integer",
 445                nullable: false,
 446                defaultValue: 0);
 47
 448            migrationBuilder.AddColumn<DateTime>(
 449                name: "locked_out_until",
 450                schema: "heimdall",
 451                table: "person",
 452                type: "timestamp with time zone",
 453                nullable: true);
 54
 55            // FR-GO-07, Google User half: the address is unique within the scope, compared the way
 56            // the application compares it. The index this replaces was over the raw column, so
 57            // "bob@x.com" and "Bob@x.com" were two free addresses to the database and one taken
 58            // address to GoogleSignInCommandHandler.
 59            //
 60            // Logically deleted rows are deliberately included, matching the handler: FR-GO-12 keeps
 61            // a deleted Google User addressable rather than erasing it, so the address it holds stays
 62            // taken until UC-29 removes the row for good.
 463            migrationBuilder.Sql(
 464                """
 465                DROP INDEX heimdall.ix_google_user_scope_id_email;
 466
 467                CREATE UNIQUE INDEX ix_google_user_scope_id_email
 468                    ON heimdall.google_user (scope_id, LOWER(email));
 469                """);
 70
 71            // FR-PE-09, administrator half: unique system-wide across live ScopeAdmins (role 2) and
 72            // SystemAdmins (role 1). This is the enforcement, not a duplicate of the handlers' check
 73            // — a check-then-insert cannot be, since two concurrent creates both read "free" before
 74            // either writes, and the loser of that race becomes a person who can never log in
 75            // (UC-11's admin lookup resolves one row and stops).
 76            //
 77            // If this statement fails on an existing database, the data already violates FR-PE-09 and
 78            // the duplicates have to be resolved before the API can enforce it. Failing the migration
 79            // is the point: creating the index "if possible" would leave the invariant unenforced
 80            // with nothing to say so.
 81            //
 82            // The User half of FR-PE-09 — unique per scope — is not here and cannot be: the scope
 83            // lives in SCOPE_USER, and a PostgreSQL index covers one table. It stays enforced in the
 84            // application layer; see PersonDbMap for what closing its race would require.
 485            migrationBuilder.Sql(
 486                """
 487                CREATE UNIQUE INDEX ux_person_admin_email
 488                    ON heimdall.person (LOWER(email))
 489                    WHERE role_id IN (1, 2) AND is_deleted = false;
 490                """);
 491        }
 92
 93        /// <inheritdoc />
 94        protected override void Down(MigrationBuilder migrationBuilder)
 95        {
 096            migrationBuilder.Sql(
 097                """
 098                DROP INDEX heimdall.ux_person_admin_email;
 099
 0100                DROP INDEX heimdall.ix_google_user_scope_id_email;
 0101
 0102                CREATE UNIQUE INDEX ix_google_user_scope_id_email
 0103                    ON heimdall.google_user (scope_id, email);
 0104                """);
 105
 0106            migrationBuilder.DropColumn(
 0107                name: "failed_attempts",
 0108                schema: "heimdall",
 0109                table: "two_factor_email_code");
 110
 0111            migrationBuilder.DropColumn(
 0112                name: "last_totp_time_step_used",
 0113                schema: "heimdall",
 0114                table: "two_factor_auth");
 115
 0116            migrationBuilder.DropColumn(
 0117                name: "failed_login_attempts",
 0118                schema: "heimdall",
 0119                table: "person");
 120
 0121            migrationBuilder.DropColumn(
 0122                name: "locked_out_until",
 0123                schema: "heimdall",
 0124                table: "person");
 0125        }
 126    }
 127}

/home/runner/work/heimdall-api/heimdall-api/src/Infrastructure/ArturRios.Heimdall.Data/Migrations/20260814160156_AddBruteForceGuardsAndEmailUniqueness.Designer.cs

#LineLine coverage
 1// <auto-generated />
 2using System;
 3using ArturRios.Heimdall.Data.Configuration;
 4using Microsoft.EntityFrameworkCore;
 5using Microsoft.EntityFrameworkCore.Infrastructure;
 6using Microsoft.EntityFrameworkCore.Migrations;
 7using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
 8using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
 9
 10#nullable disable
 11
 12namespace ArturRios.Heimdall.Data.Migrations
 13{
 14    [DbContext(typeof(AppDbContext))]
 15    [Migration("20260814160156_AddBruteForceGuardsAndEmailUniqueness")]
 16    partial class AddBruteForceGuardsAndEmailUniqueness
 17    {
 18        /// <inheritdoc />
 19        protected override void BuildTargetModel(ModelBuilder modelBuilder)
 20        {
 21#pragma warning disable 612, 618
 222            modelBuilder
 223                .HasDefaultSchema("heimdall")
 224                .HasAnnotation("ProductVersion", "10.0.11")
 225                .HasAnnotation("Relational:MaxIdentifierLength", 63);
 26
 227            NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
 28
 229            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.Application", b =>
 230                {
 231                    b.Property<long>("Id")
 232                        .ValueGeneratedOnAdd()
 233                        .HasColumnType("bigint")
 234                        .HasColumnName("id")
 235                        .HasColumnOrder(1);
 236
 237                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
 238
 239                    b.Property<DateTime>("CreatedAt")
 240                        .ValueGeneratedOnAdd()
 241                        .HasColumnType("timestamp with time zone")
 242                        .HasColumnName("created_at")
 243                        .HasDefaultValueSql("now()");
 244
 245                    b.Property<bool>("IsDeleted")
 246                        .ValueGeneratedOnAdd()
 247                        .HasColumnType("boolean")
 248                        .HasDefaultValue(false)
 249                        .HasColumnName("is_deleted");
 250
 251                    b.Property<string>("Name")
 252                        .IsRequired()
 253                        .HasMaxLength(200)
 254                        .HasColumnType("character varying(200)")
 255                        .HasColumnName("name");
 256
 257                    b.Property<long>("OwnerId")
 258                        .HasColumnType("bigint")
 259                        .HasColumnName("owner_id");
 260
 261                    b.Property<Guid>("PublicId")
 262                        .HasColumnType("uuid")
 263                        .HasColumnName("public_id");
 264
 265                    b.Property<long>("ScopeId")
 266                        .HasColumnType("bigint")
 267                        .HasColumnName("scope_id");
 268
 269                    b.Property<DateTime>("UpdatedAt")
 270                        .ValueGeneratedOnAdd()
 271                        .HasColumnType("timestamp with time zone")
 272                        .HasColumnName("updated_at")
 273                        .HasDefaultValueSql("now()");
 274
 275                    b.HasKey("Id")
 276                        .HasName("pk_application");
 277
 278                    b.HasIndex("OwnerId")
 279                        .HasDatabaseName("ix_application_owner_id");
 280
 281                    b.HasIndex("PublicId")
 282                        .IsUnique()
 283                        .HasDatabaseName("ix_application_public_id");
 284
 285                    b.HasIndex("ScopeId")
 286                        .HasDatabaseName("ix_application_scope_id");
 287
 288                    b.ToTable("application", "heimdall");
 489                });
 90
 291            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.AuditLog", b =>
 292                {
 293                    b.Property<long>("Id")
 294                        .ValueGeneratedOnAdd()
 295                        .HasColumnType("bigint")
 296                        .HasColumnName("id")
 297                        .HasColumnOrder(1);
 298
 299                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
 2100
 2101                    b.Property<string>("Action")
 2102                        .IsRequired()
 2103                        .HasMaxLength(200)
 2104                        .HasColumnType("character varying(200)")
 2105                        .HasColumnName("action");
 2106
 2107                    b.Property<Guid?>("ActorPersonId")
 2108                        .HasColumnType("uuid")
 2109                        .HasColumnName("actor_person_id");
 2110
 2111                    b.Property<int?>("ActorRole")
 2112                        .HasColumnType("integer")
 2113                        .HasColumnName("actor_role");
 2114
 2115                    b.Property<DateTime>("CreatedAt")
 2116                        .ValueGeneratedOnAdd()
 2117                        .HasColumnType("timestamp with time zone")
 2118                        .HasColumnName("created_at")
 2119                        .HasDefaultValueSql("now()");
 2120
 2121                    b.Property<Guid>("PublicId")
 2122                        .HasColumnType("uuid")
 2123                        .HasColumnName("public_id");
 2124
 2125                    b.Property<Guid?>("TargetId")
 2126                        .HasColumnType("uuid")
 2127                        .HasColumnName("target_id");
 2128
 2129                    b.HasKey("Id")
 2130                        .HasName("pk_audit_log");
 2131
 2132                    b.HasIndex("ActorPersonId")
 2133                        .HasDatabaseName("ix_audit_log_actor_person_id");
 2134
 2135                    b.HasIndex("CreatedAt")
 2136                        .HasDatabaseName("ix_audit_log_created_at");
 2137
 2138                    b.HasIndex("PublicId")
 2139                        .IsUnique()
 2140                        .HasDatabaseName("ix_audit_log_public_id");
 2141
 2142                    b.ToTable("audit_log", "heimdall");
 4143                });
 144
 2145            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.EmailVerificationToken", b =>
 2146                {
 2147                    b.Property<long>("Id")
 2148                        .ValueGeneratedOnAdd()
 2149                        .HasColumnType("bigint")
 2150                        .HasColumnName("id")
 2151                        .HasColumnOrder(1);
 2152
 2153                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
 2154
 2155                    b.Property<DateTime>("ExpiresAt")
 2156                        .HasColumnType("timestamp with time zone")
 2157                        .HasColumnName("expires_at");
 2158
 2159                    b.Property<long>("PersonId")
 2160                        .HasColumnType("bigint")
 2161                        .HasColumnName("person_id");
 2162
 2163                    b.Property<string>("Token")
 2164                        .IsRequired()
 2165                        .HasMaxLength(256)
 2166                        .HasColumnType("character varying(256)")
 2167                        .HasColumnName("token");
 2168
 2169                    b.Property<bool>("Used")
 2170                        .ValueGeneratedOnAdd()
 2171                        .HasColumnType("boolean")
 2172                        .HasDefaultValue(false)
 2173                        .HasColumnName("used");
 2174
 2175                    b.HasKey("Id")
 2176                        .HasName("pk_email_verification_token");
 2177
 2178                    b.HasIndex("PersonId")
 2179                        .HasDatabaseName("ix_email_verification_token_person_id");
 2180
 2181                    b.HasIndex("Token")
 2182                        .IsUnique()
 2183                        .HasDatabaseName("ix_email_verification_token_token");
 2184
 2185                    b.ToTable("email_verification_token", "heimdall");
 4186                });
 187
 2188            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.GoogleUser", b =>
 2189                {
 2190                    b.Property<long>("Id")
 2191                        .ValueGeneratedOnAdd()
 2192                        .HasColumnType("bigint")
 2193                        .HasColumnName("id")
 2194                        .HasColumnOrder(1);
 2195
 2196                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
 2197
 2198                    b.Property<DateTime>("CreatedAt")
 2199                        .ValueGeneratedOnAdd()
 2200                        .HasColumnType("timestamp with time zone")
 2201                        .HasColumnName("created_at")
 2202                        .HasDefaultValueSql("now()");
 2203
 2204                    b.Property<string>("Email")
 2205                        .IsRequired()
 2206                        .HasMaxLength(256)
 2207                        .HasColumnType("character varying(256)")
 2208                        .HasColumnName("email");
 2209
 2210                    b.Property<bool>("EmailVerified")
 2211                        .ValueGeneratedOnAdd()
 2212                        .HasColumnType("boolean")
 2213                        .HasDefaultValue(false)
 2214                        .HasColumnName("email_verified");
 2215
 2216                    b.Property<string>("GoogleId")
 2217                        .IsRequired()
 2218                        .HasMaxLength(255)
 2219                        .HasColumnType("character varying(255)")
 2220                        .HasColumnName("google_id");
 2221
 2222                    b.Property<bool>("IsDeleted")
 2223                        .ValueGeneratedOnAdd()
 2224                        .HasColumnType("boolean")
 2225                        .HasDefaultValue(false)
 2226                        .HasColumnName("is_deleted");
 2227
 2228                    b.Property<string>("Name")
 2229                        .IsRequired()
 2230                        .HasMaxLength(200)
 2231                        .HasColumnType("character varying(200)")
 2232                        .HasColumnName("name");
 2233
 2234                    b.Property<string>("ProfilePictureUrl")
 2235                        .HasMaxLength(2048)
 2236                        .HasColumnType("character varying(2048)")
 2237                        .HasColumnName("profile_picture_url");
 2238
 2239                    b.Property<Guid>("PublicId")
 2240                        .HasColumnType("uuid")
 2241                        .HasColumnName("public_id");
 2242
 2243                    b.Property<long>("ScopeId")
 2244                        .HasColumnType("bigint")
 2245                        .HasColumnName("scope_id");
 2246
 2247                    b.Property<DateTime>("UpdatedAt")
 2248                        .ValueGeneratedOnAdd()
 2249                        .HasColumnType("timestamp with time zone")
 2250                        .HasColumnName("updated_at")
 2251                        .HasDefaultValueSql("now()");
 2252
 2253                    b.HasKey("Id")
 2254                        .HasName("pk_google_user");
 2255
 2256                    b.HasIndex("PublicId")
 2257                        .IsUnique()
 2258                        .HasDatabaseName("ix_google_user_public_id");
 2259
 2260                    b.HasIndex("ScopeId", "Email")
 2261                        .IsUnique()
 2262                        .HasDatabaseName("ix_google_user_scope_id_email");
 2263
 2264                    b.HasIndex("ScopeId", "GoogleId")
 2265                        .IsUnique()
 2266                        .HasDatabaseName("ix_google_user_scope_id_google_id");
 2267
 2268                    b.ToTable("google_user", "heimdall");
 4269                });
 270
 2271            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.PasswordResetToken", b =>
 2272                {
 2273                    b.Property<long>("Id")
 2274                        .ValueGeneratedOnAdd()
 2275                        .HasColumnType("bigint")
 2276                        .HasColumnName("id")
 2277                        .HasColumnOrder(1);
 2278
 2279                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
 2280
 2281                    b.Property<DateTime>("ExpiresAt")
 2282                        .HasColumnType("timestamp with time zone")
 2283                        .HasColumnName("expires_at");
 2284
 2285                    b.Property<long>("PersonId")
 2286                        .HasColumnType("bigint")
 2287                        .HasColumnName("person_id");
 2288
 2289                    b.Property<string>("Token")
 2290                        .IsRequired()
 2291                        .HasMaxLength(256)
 2292                        .HasColumnType("character varying(256)")
 2293                        .HasColumnName("token");
 2294
 2295                    b.Property<bool>("Used")
 2296                        .ValueGeneratedOnAdd()
 2297                        .HasColumnType("boolean")
 2298                        .HasDefaultValue(false)
 2299                        .HasColumnName("used");
 2300
 2301                    b.HasKey("Id")
 2302                        .HasName("pk_password_reset_token");
 2303
 2304                    b.HasIndex("PersonId")
 2305                        .HasDatabaseName("ix_password_reset_token_person_id");
 2306
 2307                    b.HasIndex("Token")
 2308                        .IsUnique()
 2309                        .HasDatabaseName("ix_password_reset_token_token");
 2310
 2311                    b.ToTable("password_reset_token", "heimdall");
 4312                });
 313
 2314            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.Person", b =>
 2315                {
 2316                    b.Property<long>("Id")
 2317                        .ValueGeneratedOnAdd()
 2318                        .HasColumnType("bigint")
 2319                        .HasColumnName("id")
 2320                        .HasColumnOrder(1);
 2321
 2322                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
 2323
 2324                    b.Property<DateTime>("CreatedAt")
 2325                        .ValueGeneratedOnAdd()
 2326                        .HasColumnType("timestamp with time zone")
 2327                        .HasColumnName("created_at")
 2328                        .HasDefaultValueSql("now()");
 2329
 2330                    b.Property<string>("Email")
 2331                        .IsRequired()
 2332                        .HasMaxLength(256)
 2333                        .HasColumnType("character varying(256)")
 2334                        .HasColumnName("email");
 2335
 2336                    b.Property<bool>("EmailVerified")
 2337                        .ValueGeneratedOnAdd()
 2338                        .HasColumnType("boolean")
 2339                        .HasDefaultValue(false)
 2340                        .HasColumnName("email_verified");
 2341
 2342                    b.Property<int>("FailedLoginAttempts")
 2343                        .HasColumnType("integer")
 2344                        .HasColumnName("failed_login_attempts");
 2345
 2346                    b.Property<bool>("IsDeleted")
 2347                        .ValueGeneratedOnAdd()
 2348                        .HasColumnType("boolean")
 2349                        .HasDefaultValue(false)
 2350                        .HasColumnName("is_deleted");
 2351
 2352                    b.Property<DateTime?>("LockedOutUntil")
 2353                        .HasColumnType("timestamp with time zone")
 2354                        .HasColumnName("locked_out_until");
 2355
 2356                    b.Property<string>("Name")
 2357                        .IsRequired()
 2358                        .HasMaxLength(200)
 2359                        .HasColumnType("character varying(200)")
 2360                        .HasColumnName("name");
 2361
 2362                    b.Property<byte[]>("PasswordHash")
 2363                        .IsRequired()
 2364                        .HasColumnType("bytea")
 2365                        .HasColumnName("password_hash");
 2366
 2367                    b.Property<Guid>("PublicId")
 2368                        .HasColumnType("uuid")
 2369                        .HasColumnName("public_id");
 2370
 2371                    b.Property<long>("RoleId")
 2372                        .HasColumnType("bigint")
 2373                        .HasColumnName("role_id");
 2374
 2375                    b.Property<byte[]>("Salt")
 2376                        .IsRequired()
 2377                        .HasColumnType("bytea")
 2378                        .HasColumnName("salt");
 2379
 2380                    b.Property<DateTime>("UpdatedAt")
 2381                        .ValueGeneratedOnAdd()
 2382                        .HasColumnType("timestamp with time zone")
 2383                        .HasColumnName("updated_at")
 2384                        .HasDefaultValueSql("now()");
 2385
 2386                    b.HasKey("Id")
 2387                        .HasName("pk_person");
 2388
 2389                    b.HasIndex("Email")
 2390                        .HasDatabaseName("ix_person_email");
 2391
 2392                    b.HasIndex("PublicId")
 2393                        .IsUnique()
 2394                        .HasDatabaseName("ix_person_public_id");
 2395
 2396                    b.HasIndex("RoleId")
 2397                        .HasDatabaseName("ix_person_role_id");
 2398
 2399                    b.ToTable("person", "heimdall");
 4400                });
 401
 2402            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.Role", b =>
 2403                {
 2404                    b.Property<long>("Id")
 2405                        .HasColumnType("bigint")
 2406                        .HasColumnName("id")
 2407                        .HasColumnOrder(1);
 2408
 2409                    b.Property<string>("Description")
 2410                        .HasMaxLength(500)
 2411                        .HasColumnType("character varying(500)")
 2412                        .HasColumnName("description");
 2413
 2414                    b.Property<string>("Name")
 2415                        .IsRequired()
 2416                        .HasMaxLength(200)
 2417                        .HasColumnType("character varying(200)")
 2418                        .HasColumnName("name");
 2419
 2420                    b.Property<Guid>("PublicId")
 2421                        .HasColumnType("uuid")
 2422                        .HasColumnName("public_id");
 2423
 2424                    b.HasKey("Id")
 2425                        .HasName("pk_role");
 2426
 2427                    b.HasIndex("Name")
 2428                        .IsUnique()
 2429                        .HasDatabaseName("ix_role_name");
 2430
 2431                    b.HasIndex("PublicId")
 2432                        .IsUnique()
 2433                        .HasDatabaseName("ix_role_public_id");
 2434
 2435                    b.ToTable("role", "heimdall");
 4436                });
 437
 2438            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.Scope", b =>
 2439                {
 2440                    b.Property<long>("Id")
 2441                        .ValueGeneratedOnAdd()
 2442                        .HasColumnType("bigint")
 2443                        .HasColumnName("id")
 2444                        .HasColumnOrder(1);
 2445
 2446                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
 2447
 2448                    b.Property<DateTime>("CreatedAt")
 2449                        .ValueGeneratedOnAdd()
 2450                        .HasColumnType("timestamp with time zone")
 2451                        .HasColumnName("created_at")
 2452                        .HasDefaultValueSql("now()");
 2453
 2454                    b.Property<string>("Description")
 2455                        .HasMaxLength(500)
 2456                        .HasColumnType("character varying(500)")
 2457                        .HasColumnName("description");
 2458
 2459                    b.Property<bool>("GoogleSignInEnabled")
 2460                        .ValueGeneratedOnAdd()
 2461                        .HasColumnType("boolean")
 2462                        .HasDefaultValue(false)
 2463                        .HasColumnName("google_sign_in_enabled");
 2464
 2465                    b.Property<bool>("IsDeleted")
 2466                        .ValueGeneratedOnAdd()
 2467                        .HasColumnType("boolean")
 2468                        .HasDefaultValue(false)
 2469                        .HasColumnName("is_deleted");
 2470
 2471                    b.Property<string>("Name")
 2472                        .IsRequired()
 2473                        .HasMaxLength(200)
 2474                        .HasColumnType("character varying(200)")
 2475                        .HasColumnName("name");
 2476
 2477                    b.Property<Guid>("PublicId")
 2478                        .HasColumnType("uuid")
 2479                        .HasColumnName("public_id");
 2480
 2481                    b.Property<DateTime>("UpdatedAt")
 2482                        .ValueGeneratedOnAdd()
 2483                        .HasColumnType("timestamp with time zone")
 2484                        .HasColumnName("updated_at")
 2485                        .HasDefaultValueSql("now()");
 2486
 2487                    b.HasKey("Id")
 2488                        .HasName("pk_scope");
 2489
 2490                    b.HasIndex("Name")
 2491                        .IsUnique()
 2492                        .HasDatabaseName("ix_scope_name");
 2493
 2494                    b.HasIndex("PublicId")
 2495                        .IsUnique()
 2496                        .HasDatabaseName("ix_scope_public_id");
 2497
 2498                    b.ToTable("scope", "heimdall");
 4499                });
 500
 2501            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.ScopeOwner", b =>
 2502                {
 2503                    b.Property<long>("ScopeId")
 2504                        .HasColumnType("bigint")
 2505                        .HasColumnName("scope_id");
 2506
 2507                    b.Property<long>("PersonId")
 2508                        .HasColumnType("bigint")
 2509                        .HasColumnName("person_id");
 2510
 2511                    b.HasKey("ScopeId", "PersonId")
 2512                        .HasName("pk_scope_owner");
 2513
 2514                    b.HasIndex("PersonId")
 2515                        .HasDatabaseName("ix_scope_owner_person_id");
 2516
 2517                    b.ToTable("scope_owner", "heimdall");
 4518                });
 519
 2520            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.ScopePermission", b =>
 2521                {
 2522                    b.Property<long>("Id")
 2523                        .ValueGeneratedOnAdd()
 2524                        .HasColumnType("bigint")
 2525                        .HasColumnName("id")
 2526                        .HasColumnOrder(1);
 2527
 2528                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
 2529
 2530                    b.Property<DateTime>("CreatedAt")
 2531                        .ValueGeneratedOnAdd()
 2532                        .HasColumnType("timestamp with time zone")
 2533                        .HasColumnName("created_at")
 2534                        .HasDefaultValueSql("now()");
 2535
 2536                    b.Property<string>("Description")
 2537                        .HasMaxLength(500)
 2538                        .HasColumnType("character varying(500)")
 2539                        .HasColumnName("description");
 2540
 2541                    b.Property<bool>("IncludeAsJwtClaim")
 2542                        .ValueGeneratedOnAdd()
 2543                        .HasColumnType("boolean")
 2544                        .HasDefaultValue(false)
 2545                        .HasColumnName("include_as_jwt_claim");
 2546
 2547                    b.Property<bool>("IsDeleted")
 2548                        .ValueGeneratedOnAdd()
 2549                        .HasColumnType("boolean")
 2550                        .HasDefaultValue(false)
 2551                        .HasColumnName("is_deleted");
 2552
 2553                    b.Property<string>("Name")
 2554                        .IsRequired()
 2555                        .HasMaxLength(200)
 2556                        .HasColumnType("character varying(200)")
 2557                        .HasColumnName("name");
 2558
 2559                    b.Property<Guid>("PublicId")
 2560                        .HasColumnType("uuid")
 2561                        .HasColumnName("public_id");
 2562
 2563                    b.Property<long>("ScopeId")
 2564                        .HasColumnType("bigint")
 2565                        .HasColumnName("scope_id");
 2566
 2567                    b.Property<DateTime>("UpdatedAt")
 2568                        .ValueGeneratedOnAdd()
 2569                        .HasColumnType("timestamp with time zone")
 2570                        .HasColumnName("updated_at")
 2571                        .HasDefaultValueSql("now()");
 2572
 2573                    b.HasKey("Id")
 2574                        .HasName("pk_scope_permission");
 2575
 2576                    b.HasIndex("PublicId")
 2577                        .IsUnique()
 2578                        .HasDatabaseName("ix_scope_permission_public_id");
 2579
 2580                    b.HasIndex("ScopeId")
 2581                        .HasDatabaseName("ix_scope_permission_scope_id");
 2582
 2583                    b.ToTable("scope_permission", "heimdall");
 4584                });
 585
 2586            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.ScopeUser", b =>
 2587                {
 2588                    b.Property<long>("ScopeId")
 2589                        .HasColumnType("bigint")
 2590                        .HasColumnName("scope_id");
 2591
 2592                    b.Property<long>("PersonId")
 2593                        .HasColumnType("bigint")
 2594                        .HasColumnName("person_id");
 2595
 2596                    b.HasKey("ScopeId", "PersonId")
 2597                        .HasName("pk_scope_user");
 2598
 2599                    b.HasIndex("PersonId")
 2600                        .IsUnique()
 2601                        .HasDatabaseName("ix_scope_user_person_id");
 2602
 2603                    b.ToTable("scope_user", "heimdall");
 4604                });
 605
 2606            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.TwoFactorAuth", b =>
 2607                {
 2608                    b.Property<long>("Id")
 2609                        .ValueGeneratedOnAdd()
 2610                        .HasColumnType("bigint")
 2611                        .HasColumnName("id")
 2612                        .HasColumnOrder(1);
 2613
 2614                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
 2615
 2616                    b.Property<bool>("AppEnabled")
 2617                        .ValueGeneratedOnAdd()
 2618                        .HasColumnType("boolean")
 2619                        .HasDefaultValue(false)
 2620                        .HasColumnName("app_enabled");
 2621
 2622                    b.Property<DateTime>("CreatedAt")
 2623                        .ValueGeneratedOnAdd()
 2624                        .HasColumnType("timestamp with time zone")
 2625                        .HasColumnName("created_at")
 2626                        .HasDefaultValueSql("now()");
 2627
 2628                    b.Property<bool>("EmailEnabled")
 2629                        .ValueGeneratedOnAdd()
 2630                        .HasColumnType("boolean")
 2631                        .HasDefaultValue(false)
 2632                        .HasColumnName("email_enabled");
 2633
 2634                    b.Property<bool>("IsActive")
 2635                        .ValueGeneratedOnAdd()
 2636                        .HasColumnType("boolean")
 2637                        .HasDefaultValue(false)
 2638                        .HasColumnName("is_active");
 2639
 2640                    b.Property<long?>("LastTotpTimeStepUsed")
 2641                        .HasColumnType("bigint")
 2642                        .HasColumnName("last_totp_time_step_used");
 2643
 2644                    b.Property<long>("PersonId")
 2645                        .HasColumnType("bigint")
 2646                        .HasColumnName("person_id");
 2647
 2648                    b.Property<byte[]>("TotpSecretEncrypted")
 2649                        .HasColumnType("bytea")
 2650                        .HasColumnName("totp_secret_encrypted");
 2651
 2652                    b.Property<DateTime>("UpdatedAt")
 2653                        .ValueGeneratedOnAdd()
 2654                        .HasColumnType("timestamp with time zone")
 2655                        .HasColumnName("updated_at")
 2656                        .HasDefaultValueSql("now()");
 2657
 2658                    b.HasKey("Id")
 2659                        .HasName("pk_two_factor_auth");
 2660
 2661                    b.HasIndex("PersonId")
 2662                        .IsUnique()
 2663                        .HasDatabaseName("ix_two_factor_auth_person_id");
 2664
 2665                    b.ToTable("two_factor_auth", "heimdall");
 4666                });
 667
 2668            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.TwoFactorEmailCode", b =>
 2669                {
 2670                    b.Property<long>("Id")
 2671                        .ValueGeneratedOnAdd()
 2672                        .HasColumnType("bigint")
 2673                        .HasColumnName("id")
 2674                        .HasColumnOrder(1);
 2675
 2676                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
 2677
 2678                    b.Property<byte[]>("CodeHash")
 2679                        .IsRequired()
 2680                        .HasColumnType("bytea")
 2681                        .HasColumnName("code_hash");
 2682
 2683                    b.Property<DateTime>("CreatedAt")
 2684                        .ValueGeneratedOnAdd()
 2685                        .HasColumnType("timestamp with time zone")
 2686                        .HasColumnName("created_at")
 2687                        .HasDefaultValueSql("now()");
 2688
 2689                    b.Property<DateTime>("ExpiresAt")
 2690                        .HasColumnType("timestamp with time zone")
 2691                        .HasColumnName("expires_at");
 2692
 2693                    b.Property<int>("FailedAttempts")
 2694                        .HasColumnType("integer")
 2695                        .HasColumnName("failed_attempts");
 2696
 2697                    b.Property<byte[]>("Salt")
 2698                        .IsRequired()
 2699                        .HasColumnType("bytea")
 2700                        .HasColumnName("salt");
 2701
 2702                    b.Property<long>("TwoFactorAuthId")
 2703                        .HasColumnType("bigint")
 2704                        .HasColumnName("two_factor_auth_id");
 2705
 2706                    b.Property<bool>("Used")
 2707                        .ValueGeneratedOnAdd()
 2708                        .HasColumnType("boolean")
 2709                        .HasDefaultValue(false)
 2710                        .HasColumnName("used");
 2711
 2712                    b.HasKey("Id")
 2713                        .HasName("pk_two_factor_email_code");
 2714
 2715                    b.HasIndex("TwoFactorAuthId")
 2716                        .HasDatabaseName("ix_two_factor_email_code_two_factor_auth_id");
 2717
 2718                    b.ToTable("two_factor_email_code", "heimdall");
 4719                });
 720
 2721            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.TwoFactorRecoveryCode", b =>
 2722                {
 2723                    b.Property<long>("Id")
 2724                        .ValueGeneratedOnAdd()
 2725                        .HasColumnType("bigint")
 2726                        .HasColumnName("id")
 2727                        .HasColumnOrder(1);
 2728
 2729                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
 2730
 2731                    b.Property<byte[]>("CodeHash")
 2732                        .IsRequired()
 2733                        .HasColumnType("bytea")
 2734                        .HasColumnName("code_hash");
 2735
 2736                    b.Property<DateTime>("CreatedAt")
 2737                        .ValueGeneratedOnAdd()
 2738                        .HasColumnType("timestamp with time zone")
 2739                        .HasColumnName("created_at")
 2740                        .HasDefaultValueSql("now()");
 2741
 2742                    b.Property<long>("TwoFactorAuthId")
 2743                        .HasColumnType("bigint")
 2744                        .HasColumnName("two_factor_auth_id");
 2745
 2746                    b.Property<bool>("Used")
 2747                        .ValueGeneratedOnAdd()
 2748                        .HasColumnType("boolean")
 2749                        .HasDefaultValue(false)
 2750                        .HasColumnName("used");
 2751
 2752                    b.Property<DateTime?>("UsedAt")
 2753                        .HasColumnType("timestamp with time zone")
 2754                        .HasColumnName("used_at");
 2755
 2756                    b.HasKey("Id")
 2757                        .HasName("pk_two_factor_recovery_code");
 2758
 2759                    b.HasIndex("TwoFactorAuthId")
 2760                        .HasDatabaseName("ix_two_factor_recovery_code_two_factor_auth_id");
 2761
 2762                    b.ToTable("two_factor_recovery_code", "heimdall");
 4763                });
 764
 2765            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.Application", b =>
 2766                {
 2767                    b.HasOne("ArturRios.Heimdall.Domain.Entities.Person", "Owner")
 2768                        .WithMany("OwnedApplications")
 2769                        .HasForeignKey("OwnerId")
 2770                        .OnDelete(DeleteBehavior.Cascade)
 2771                        .IsRequired()
 2772                        .HasConstraintName("fk_application_person_owner_id");
 2773
 2774                    b.HasOne("ArturRios.Heimdall.Domain.Entities.Scope", "Scope")
 2775                        .WithMany("Applications")
 2776                        .HasForeignKey("ScopeId")
 2777                        .OnDelete(DeleteBehavior.Cascade)
 2778                        .IsRequired()
 2779                        .HasConstraintName("fk_application_scope_scope_id");
 2780
 2781                    b.Navigation("Owner");
 2782
 2783                    b.Navigation("Scope");
 4784                });
 785
 2786            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.EmailVerificationToken", b =>
 2787                {
 2788                    b.HasOne("ArturRios.Heimdall.Domain.Entities.Person", "Person")
 2789                        .WithMany("EmailVerificationTokens")
 2790                        .HasForeignKey("PersonId")
 2791                        .OnDelete(DeleteBehavior.Cascade)
 2792                        .IsRequired()
 2793                        .HasConstraintName("fk_email_verification_token_person_person_id");
 2794
 2795                    b.Navigation("Person");
 4796                });
 797
 2798            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.GoogleUser", b =>
 2799                {
 2800                    b.HasOne("ArturRios.Heimdall.Domain.Entities.Scope", "Scope")
 2801                        .WithMany("GoogleUsers")
 2802                        .HasForeignKey("ScopeId")
 2803                        .OnDelete(DeleteBehavior.Cascade)
 2804                        .IsRequired()
 2805                        .HasConstraintName("fk_google_user_scope_scope_id");
 2806
 2807                    b.Navigation("Scope");
 4808                });
 809
 2810            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.PasswordResetToken", b =>
 2811                {
 2812                    b.HasOne("ArturRios.Heimdall.Domain.Entities.Person", "Person")
 2813                        .WithMany("PasswordResetTokens")
 2814                        .HasForeignKey("PersonId")
 2815                        .OnDelete(DeleteBehavior.Cascade)
 2816                        .IsRequired()
 2817                        .HasConstraintName("fk_password_reset_token_person_person_id");
 2818
 2819                    b.Navigation("Person");
 4820                });
 821
 2822            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.Person", b =>
 2823                {
 2824                    b.HasOne("ArturRios.Heimdall.Domain.Entities.Role", "Role")
 2825                        .WithMany("Persons")
 2826                        .HasForeignKey("RoleId")
 2827                        .OnDelete(DeleteBehavior.Restrict)
 2828                        .IsRequired()
 2829                        .HasConstraintName("fk_person_roles_role_id");
 2830
 2831                    b.Navigation("Role");
 4832                });
 833
 2834            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.ScopeOwner", b =>
 2835                {
 2836                    b.HasOne("ArturRios.Heimdall.Domain.Entities.Person", "Person")
 2837                        .WithMany("ScopeOwnerships")
 2838                        .HasForeignKey("PersonId")
 2839                        .OnDelete(DeleteBehavior.Cascade)
 2840                        .IsRequired()
 2841                        .HasConstraintName("fk_scope_owner_person_person_id");
 2842
 2843                    b.HasOne("ArturRios.Heimdall.Domain.Entities.Scope", "Scope")
 2844                        .WithMany("Owners")
 2845                        .HasForeignKey("ScopeId")
 2846                        .OnDelete(DeleteBehavior.Cascade)
 2847                        .IsRequired()
 2848                        .HasConstraintName("fk_scope_owner_scope_scope_id");
 2849
 2850                    b.Navigation("Person");
 2851
 2852                    b.Navigation("Scope");
 4853                });
 854
 2855            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.ScopePermission", b =>
 2856                {
 2857                    b.HasOne("ArturRios.Heimdall.Domain.Entities.Scope", "Scope")
 2858                        .WithMany("ScopePermissions")
 2859                        .HasForeignKey("ScopeId")
 2860                        .OnDelete(DeleteBehavior.Cascade)
 2861                        .IsRequired()
 2862                        .HasConstraintName("fk_scope_permission_scope_scope_id");
 2863
 2864                    b.Navigation("Scope");
 4865                });
 866
 2867            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.ScopeUser", b =>
 2868                {
 2869                    b.HasOne("ArturRios.Heimdall.Domain.Entities.Person", "Person")
 2870                        .WithOne("ScopeMembership")
 2871                        .HasForeignKey("ArturRios.Heimdall.Domain.Entities.ScopeUser", "PersonId")
 2872                        .OnDelete(DeleteBehavior.Cascade)
 2873                        .IsRequired()
 2874                        .HasConstraintName("fk_scope_user_person_person_id");
 2875
 2876                    b.HasOne("ArturRios.Heimdall.Domain.Entities.Scope", "Scope")
 2877                        .WithMany("Users")
 2878                        .HasForeignKey("ScopeId")
 2879                        .OnDelete(DeleteBehavior.Cascade)
 2880                        .IsRequired()
 2881                        .HasConstraintName("fk_scope_user_scope_scope_id");
 2882
 2883                    b.Navigation("Person");
 2884
 2885                    b.Navigation("Scope");
 4886                });
 887
 2888            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.TwoFactorAuth", b =>
 2889                {
 2890                    b.HasOne("ArturRios.Heimdall.Domain.Entities.Person", "Person")
 2891                        .WithOne("TwoFactorAuth")
 2892                        .HasForeignKey("ArturRios.Heimdall.Domain.Entities.TwoFactorAuth", "PersonId")
 2893                        .OnDelete(DeleteBehavior.Cascade)
 2894                        .IsRequired()
 2895                        .HasConstraintName("fk_two_factor_auth_person_person_id");
 2896
 2897                    b.Navigation("Person");
 4898                });
 899
 2900            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.TwoFactorEmailCode", b =>
 2901                {
 2902                    b.HasOne("ArturRios.Heimdall.Domain.Entities.TwoFactorAuth", "TwoFactorAuth")
 2903                        .WithMany("EmailCodes")
 2904                        .HasForeignKey("TwoFactorAuthId")
 2905                        .OnDelete(DeleteBehavior.Cascade)
 2906                        .IsRequired()
 2907                        .HasConstraintName("fk_two_factor_email_code_two_factor_auth_two_factor_auth_id");
 2908
 2909                    b.Navigation("TwoFactorAuth");
 4910                });
 911
 2912            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.TwoFactorRecoveryCode", b =>
 2913                {
 2914                    b.HasOne("ArturRios.Heimdall.Domain.Entities.TwoFactorAuth", "TwoFactorAuth")
 2915                        .WithMany("RecoveryCodes")
 2916                        .HasForeignKey("TwoFactorAuthId")
 2917                        .OnDelete(DeleteBehavior.Cascade)
 2918                        .IsRequired()
 2919                        .HasConstraintName("fk_two_factor_recovery_code_two_factor_auth_two_factor_auth_id");
 2920
 2921                    b.Navigation("TwoFactorAuth");
 4922                });
 923
 2924            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.Person", b =>
 2925                {
 2926                    b.Navigation("EmailVerificationTokens");
 2927
 2928                    b.Navigation("OwnedApplications");
 2929
 2930                    b.Navigation("PasswordResetTokens");
 2931
 2932                    b.Navigation("ScopeMembership");
 2933
 2934                    b.Navigation("ScopeOwnerships");
 2935
 2936                    b.Navigation("TwoFactorAuth");
 4937                });
 938
 2939            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.Role", b =>
 2940                {
 2941                    b.Navigation("Persons");
 4942                });
 943
 2944            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.Scope", b =>
 2945                {
 2946                    b.Navigation("Applications");
 2947
 2948                    b.Navigation("GoogleUsers");
 2949
 2950                    b.Navigation("Owners");
 2951
 2952                    b.Navigation("ScopePermissions");
 2953
 2954                    b.Navigation("Users");
 4955                });
 956
 2957            modelBuilder.Entity("ArturRios.Heimdall.Domain.Entities.TwoFactorAuth", b =>
 2958                {
 2959                    b.Navigation("EmailCodes");
 2960
 2961                    b.Navigation("RecoveryCodes");
 4962                });
 963#pragma warning restore 612, 618
 2964        }
 965    }
 966}