< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.Data.Registration.DataServiceCollectionExtensions
Assembly: ArturRios.Heimdall.Data
File(s): /home/ci/agent/workspace/heimdall-api-docs_main/app/src/Infrastructure/ArturRios.Heimdall.Data/Registration/DataServiceCollectionExtensions.cs
Line coverage
100%
Covered lines: 16
Uncovered lines: 0
Coverable lines: 16
Total lines: 74
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage
100%
Covered methods: 3
Fully covered methods: 3
Total methods: 3
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
AddPersistenceServices(...)100%11100%
AddDataProtectionKeyRing(...)100%11100%
AddDatabaseSeeding(...)100%11100%

File(s)

/home/ci/agent/workspace/heimdall-api-docs_main/app/src/Infrastructure/ArturRios.Heimdall.Data/Registration/DataServiceCollectionExtensions.cs

#LineLine coverage
 1using ArturRios.Heimdall.Data.Configuration;
 2using ArturRios.Heimdall.Data.Persistence;
 3using ArturRios.Heimdall.Data.Seeding;
 4using ArturRios.Heimdall.Domain.Persistence;
 5using Microsoft.AspNetCore.DataProtection;
 6using Microsoft.Extensions.DependencyInjection;
 7
 8namespace ArturRios.Heimdall.Data.Registration;
 9
 10public static class DataServiceCollectionExtensions
 11{
 12    /// <summary>
 13    ///     Registers the persistence services that spend single-use credentials, charge bounded budgets and
 14    ///     take row locks as conditional writes.
 15    /// </summary>
 16    public static IServiceCollection AddPersistenceServices(this IServiceCollection services)
 17    {
 18        // Spending single-use credentials and charging bounded budgets as one conditional UPDATE
 19        // each, so concurrent requests cannot both spend one code or lose each other's counts.
 97320        services.AddScoped<IAtomicWrites, AtomicWrites>();
 21        // UC-53's two-live-credentials limit, held under a row lock so concurrent issues cannot pass it.
 97322        services.AddScoped<IApplicationCredentialSlots, ApplicationCredentialSlots>();
 23        // NFR-21 (#195): the tombstones a hard delete writes first, idempotently, as one upsert.
 97324        services.AddScoped<IErasedSubjects, ErasedSubjects>();
 25        // FR-SR-14: the per-address budget of registration-attempt notices, charged as one upsert.
 97326        services.AddScoped<IAttemptNoticeBudgets, AttemptNoticeBudgets>();
 27        // FR-SC-17: opening, answering, withdrawing and voiding scope owner invitations, each one
 28        // conditional write, and acceptance one transaction with the ownership it grants.
 97329        services.AddScoped<IScopeOwnerInvitations, ScopeOwnerInvitations>();
 30        // #209, #212, #204: the transaction a command opens around the writes that must land together.
 97331        services.AddScoped<IWriteTransactions, WriteTransactions>();
 32        // #204: a scope's delete cascaded to its members as set-based statements, never member by member.
 97333        services.AddScoped<IScopeCascades, ScopeCascades>();
 34        // #202: the scope row locks that make a last-owner guard and its write one step.
 97335        services.AddScoped<IInvariantLocks, InvariantLocks>();
 36
 97337        return services;
 38    }
 39
 40    /// <summary>
 41    ///     Puts the Data Protection key ring in the database (UC-36, FR-2F-02).
 42    /// </summary>
 43    public static IServiceCollection AddDataProtectionKeyRing(this IServiceCollection services)
 44    {
 45        // UC-36 (FR-2F-02): the TOTP secret is encrypted at rest with ASP.NET Core's Data
 46        // Protection API before it is persisted.
 47        //
 48        // The key ring goes in the database, and the application name is fixed, because the default
 49        // is neither durable nor shared: keys land in a directory on the local filesystem that the
 50        // image does not persist and a second instance does not see. Either way the effect is the
 51        // same and it is silent — every stored TOTP secret becomes undecryptable, ITotpCodeVerifier
 52        // catches the CryptographicException and reports it as a wrong code, and every caller whose
 53        // second factor is an authenticator app is locked out of it with no indication why. That
 54        // survives a redeploy today only because a single container keeps its filesystem between
 55        // restarts; it would not survive being recreated, and it never survives a second replica
 56        // (NFR-06).
 97357        services.AddDataProtection()
 97358            .PersistKeysToDbContext<AppDbContext>()
 97359            .SetApplicationName("Heimdall");
 60
 97361        return services;
 62    }
 63
 64    /// <summary>
 65    ///     Registers the master user options and the database seeder.
 66    /// </summary>
 67    public static IServiceCollection AddDatabaseSeeding(this IServiceCollection services)
 68    {
 97369        services.AddSingleton(MasterUserOptions.FromEnvironment());
 97370        services.AddScoped<DatabaseSeeder>();
 71
 97372        return services;
 73    }
 74}