save progress
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
<LangVersion>preview</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -7,4 +7,4 @@ Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0086-M | DONE | Maintainability audit for StellaOps.Authority.Core. |
|
||||
| AUDIT-0086-T | DONE | Test coverage audit for StellaOps.Authority.Core. |
|
||||
| AUDIT-0086-A | TODO | Pending approval for changes. |
|
||||
| AUDIT-0086-A | DONE | Deterministic builder defaults, replay verifier handling, and tests. |
|
||||
|
||||
@@ -75,7 +75,7 @@ public sealed class NullVerdictManifestSigner : IVerdictManifestSigner
|
||||
public Task<SignatureVerificationResult> VerifyAsync(VerdictManifest manifest, CancellationToken ct = default)
|
||||
=> Task.FromResult(new SignatureVerificationResult
|
||||
{
|
||||
Valid = true,
|
||||
Valid = false,
|
||||
Error = "Signing disabled",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ public static class VerdictManifestSerializer
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Serialize manifest to canonical JSON (sorted keys, no indentation).
|
||||
/// Serialize manifest to deterministic JSON (stable naming policy, no indentation).
|
||||
/// </summary>
|
||||
public static string Serialize(VerdictManifest manifest)
|
||||
{
|
||||
|
||||
@@ -14,17 +14,25 @@ public sealed class VerdictManifestBuilder
|
||||
private VerdictResult? _result;
|
||||
private string? _policyHash;
|
||||
private string? _latticeVersion;
|
||||
private DateTimeOffset _evaluatedAt = DateTimeOffset.UtcNow;
|
||||
private DateTimeOffset _evaluatedAt;
|
||||
private readonly Func<string> _idGenerator;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public VerdictManifestBuilder()
|
||||
: this(() => Guid.NewGuid().ToString("n"))
|
||||
: this(() => Guid.NewGuid().ToString("n"), TimeProvider.System)
|
||||
{
|
||||
}
|
||||
|
||||
public VerdictManifestBuilder(Func<string> idGenerator)
|
||||
: this(idGenerator, TimeProvider.System)
|
||||
{
|
||||
}
|
||||
|
||||
public VerdictManifestBuilder(Func<string> idGenerator, TimeProvider timeProvider)
|
||||
{
|
||||
_idGenerator = idGenerator ?? throw new ArgumentNullException(nameof(idGenerator));
|
||||
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||
_evaluatedAt = _timeProvider.GetUtcNow();
|
||||
}
|
||||
|
||||
public VerdictManifestBuilder WithTenant(string tenant)
|
||||
@@ -74,7 +82,7 @@ public sealed class VerdictManifestBuilder
|
||||
VulnFeedSnapshotIds = SortedImmutable(vulnFeedSnapshotIds),
|
||||
VexDocumentDigests = SortedImmutable(vexDocumentDigests),
|
||||
ReachabilityGraphIds = SortedImmutable(reachabilityGraphIds ?? Enumerable.Empty<string>()),
|
||||
ClockCutoff = clockCutoff ?? DateTimeOffset.UtcNow,
|
||||
ClockCutoff = clockCutoff ?? _timeProvider.GetUtcNow(),
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -100,14 +100,8 @@ public sealed class VerdictReplayVerifier : IVerdictReplayVerifier
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(manifestId);
|
||||
|
||||
// We need to find the manifest - this requires a search across tenants
|
||||
// In practice, the caller should provide the tenant or the manifest directly
|
||||
return new ReplayVerificationResult
|
||||
{
|
||||
Success = false,
|
||||
OriginalManifest = null!,
|
||||
Error = "Use VerifyAsync(VerdictManifest) overload with the full manifest.",
|
||||
};
|
||||
throw new InvalidOperationException(
|
||||
"Verdict replay requires a full manifest or tenant context; use VerifyAsync(VerdictManifest) instead.");
|
||||
}
|
||||
|
||||
public async Task<ReplayVerificationResult> VerifyAsync(VerdictManifest manifest, CancellationToken ct = default)
|
||||
|
||||
@@ -79,5 +79,13 @@ public static class AuthorityPersistenceExtensions
|
||||
services.AddScoped<IOfflineKitAuditRepository>(sp => sp.GetRequiredService<OfflineKitAuditRepository>());
|
||||
services.AddScoped<IOfflineKitAuditEmitter, OfflineKitAuditEmitter>();
|
||||
services.AddScoped<RevocationExportStateRepository>();
|
||||
|
||||
services.AddScoped<IBootstrapInviteRepository>(sp => sp.GetRequiredService<BootstrapInviteRepository>());
|
||||
services.AddScoped<IServiceAccountRepository>(sp => sp.GetRequiredService<ServiceAccountRepository>());
|
||||
services.AddScoped<IClientRepository>(sp => sp.GetRequiredService<ClientRepository>());
|
||||
services.AddScoped<IRevocationRepository>(sp => sp.GetRequiredService<RevocationRepository>());
|
||||
services.AddScoped<ILoginAttemptRepository>(sp => sp.GetRequiredService<LoginAttemptRepository>());
|
||||
services.AddScoped<IOidcTokenRepository>(sp => sp.GetRequiredService<OidcTokenRepository>());
|
||||
services.AddScoped<IAirgapAuditRepository>(sp => sp.GetRequiredService<AirgapAuditRepository>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
/// <summary>
|
||||
/// PostgreSQL repository for airgap audit records.
|
||||
/// </summary>
|
||||
public sealed class AirgapAuditRepository : RepositoryBase<AuthorityDataSource>
|
||||
public sealed class AirgapAuditRepository : RepositoryBase<AuthorityDataSource>, IAirgapAuditRepository
|
||||
{
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.General);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
/// <summary>
|
||||
/// PostgreSQL repository for bootstrap invites.
|
||||
/// </summary>
|
||||
public sealed class BootstrapInviteRepository : RepositoryBase<AuthorityDataSource>
|
||||
public sealed class BootstrapInviteRepository : RepositoryBase<AuthorityDataSource>, IBootstrapInviteRepository
|
||||
{
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.General);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
/// <summary>
|
||||
/// PostgreSQL repository for OAuth/OpenID clients.
|
||||
/// </summary>
|
||||
public sealed class ClientRepository : RepositoryBase<AuthorityDataSource>
|
||||
public sealed class ClientRepository : RepositoryBase<AuthorityDataSource>, IClientRepository
|
||||
{
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.General);
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using StellaOps.Authority.Persistence.Postgres.Models;
|
||||
|
||||
namespace StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
|
||||
public interface IAirgapAuditRepository
|
||||
{
|
||||
Task InsertAsync(AirgapAuditEntity entity, CancellationToken cancellationToken = default);
|
||||
Task<IReadOnlyList<AirgapAuditEntity>> ListAsync(int limit, int offset, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using StellaOps.Authority.Persistence.Postgres.Models;
|
||||
|
||||
namespace StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
|
||||
public interface IBootstrapInviteRepository
|
||||
{
|
||||
Task<BootstrapInviteEntity?> FindByTokenAsync(string token, CancellationToken cancellationToken = default);
|
||||
Task InsertAsync(BootstrapInviteEntity entity, CancellationToken cancellationToken = default);
|
||||
Task<bool> TryReserveAsync(string token, string expectedType, DateTimeOffset now, string? reservedBy, CancellationToken cancellationToken = default);
|
||||
Task<bool> ReleaseAsync(string token, CancellationToken cancellationToken = default);
|
||||
Task<bool> ConsumeAsync(string token, string? consumedBy, DateTimeOffset consumedAt, CancellationToken cancellationToken = default);
|
||||
Task<IReadOnlyList<BootstrapInviteEntity>> ExpireAsync(DateTimeOffset asOf, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using StellaOps.Authority.Persistence.Postgres.Models;
|
||||
|
||||
namespace StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
|
||||
public interface IClientRepository
|
||||
{
|
||||
Task<ClientEntity?> FindByClientIdAsync(string clientId, CancellationToken cancellationToken = default);
|
||||
Task UpsertAsync(ClientEntity entity, CancellationToken cancellationToken = default);
|
||||
Task<bool> DeleteByClientIdAsync(string clientId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using StellaOps.Authority.Persistence.Postgres.Models;
|
||||
|
||||
namespace StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
|
||||
public interface ILoginAttemptRepository
|
||||
{
|
||||
Task InsertAsync(LoginAttemptEntity entity, CancellationToken cancellationToken = default);
|
||||
Task<IReadOnlyList<LoginAttemptEntity>> ListRecentAsync(string subjectId, int limit, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using StellaOps.Authority.Persistence.Postgres.Models;
|
||||
|
||||
namespace StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
|
||||
public interface IOidcTokenRepository
|
||||
{
|
||||
Task<OidcTokenEntity?> FindByTokenIdAsync(string tokenId, CancellationToken cancellationToken = default);
|
||||
Task<OidcTokenEntity?> FindByReferenceIdAsync(string referenceId, CancellationToken cancellationToken = default);
|
||||
Task<IReadOnlyList<OidcTokenEntity>> ListBySubjectAsync(string subjectId, int limit, CancellationToken cancellationToken = default);
|
||||
Task<IReadOnlyList<OidcTokenEntity>> ListByClientAsync(string clientId, int limit, int offset, CancellationToken cancellationToken = default);
|
||||
Task<IReadOnlyList<OidcTokenEntity>> ListByScopeAsync(string tenant, string scope, DateTimeOffset? issuedAfter, int limit, CancellationToken cancellationToken = default);
|
||||
Task<IReadOnlyList<OidcTokenEntity>> ListRevokedAsync(string? tenant, int limit, CancellationToken cancellationToken = default);
|
||||
Task<long> CountActiveDelegationTokensAsync(string tenant, string? serviceAccountId, DateTimeOffset now, CancellationToken cancellationToken = default);
|
||||
Task<IReadOnlyList<OidcTokenEntity>> ListActiveDelegationTokensAsync(string tenant, string? serviceAccountId, DateTimeOffset now, int limit, CancellationToken cancellationToken = default);
|
||||
Task<IReadOnlyList<OidcTokenEntity>> ListAsync(int limit, CancellationToken cancellationToken = default);
|
||||
Task UpsertAsync(OidcTokenEntity entity, CancellationToken cancellationToken = default);
|
||||
Task<bool> RevokeAsync(string tokenId, CancellationToken cancellationToken = default);
|
||||
Task<int> RevokeBySubjectAsync(string subjectId, CancellationToken cancellationToken = default);
|
||||
Task<int> RevokeByClientAsync(string clientId, CancellationToken cancellationToken = default);
|
||||
Task<OidcRefreshTokenEntity?> FindRefreshTokenAsync(string tokenId, CancellationToken cancellationToken = default);
|
||||
Task<OidcRefreshTokenEntity?> FindRefreshTokenByHandleAsync(string handle, CancellationToken cancellationToken = default);
|
||||
Task UpsertRefreshTokenAsync(OidcRefreshTokenEntity entity, CancellationToken cancellationToken = default);
|
||||
Task<bool> ConsumeRefreshTokenAsync(string tokenId, CancellationToken cancellationToken = default);
|
||||
Task<int> RevokeRefreshTokensBySubjectAsync(string subjectId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using StellaOps.Authority.Persistence.Postgres.Models;
|
||||
|
||||
namespace StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
|
||||
public interface IRevocationRepository
|
||||
{
|
||||
Task UpsertAsync(RevocationEntity entity, CancellationToken cancellationToken = default);
|
||||
Task<IReadOnlyList<RevocationEntity>> GetActiveAsync(DateTimeOffset asOf, CancellationToken cancellationToken = default);
|
||||
Task RemoveAsync(string category, string revocationId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using StellaOps.Authority.Persistence.Postgres.Models;
|
||||
|
||||
namespace StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
|
||||
public interface IServiceAccountRepository
|
||||
{
|
||||
Task<ServiceAccountEntity?> FindByAccountIdAsync(string accountId, CancellationToken cancellationToken = default);
|
||||
Task<IReadOnlyList<ServiceAccountEntity>> ListAsync(string? tenant, CancellationToken cancellationToken = default);
|
||||
Task UpsertAsync(ServiceAccountEntity entity, CancellationToken cancellationToken = default);
|
||||
Task<bool> DeleteAsync(string accountId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
/// <summary>
|
||||
/// PostgreSQL repository for login attempts.
|
||||
/// </summary>
|
||||
public sealed class LoginAttemptRepository : RepositoryBase<AuthorityDataSource>
|
||||
public sealed class LoginAttemptRepository : RepositoryBase<AuthorityDataSource>, ILoginAttemptRepository
|
||||
{
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.General);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
/// <summary>
|
||||
/// PostgreSQL repository for OpenIddict tokens and refresh tokens.
|
||||
/// </summary>
|
||||
public sealed class OidcTokenRepository : RepositoryBase<AuthorityDataSource>
|
||||
public sealed class OidcTokenRepository : RepositoryBase<AuthorityDataSource>, IOidcTokenRepository
|
||||
{
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.General);
|
||||
|
||||
@@ -69,6 +69,130 @@ public sealed class OidcTokenRepository : RepositoryBase<AuthorityDataSource>
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<OidcTokenEntity>> ListByClientAsync(string clientId, int limit, int offset, CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = """
|
||||
SELECT id, token_id, subject_id, client_id, token_type, reference_id, created_at, expires_at, redeemed_at, payload, properties
|
||||
FROM authority.oidc_tokens
|
||||
WHERE client_id = @client_id
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT @limit OFFSET @offset
|
||||
""";
|
||||
|
||||
return await QueryAsync(
|
||||
tenantId: string.Empty,
|
||||
sql: sql,
|
||||
configureCommand: cmd =>
|
||||
{
|
||||
AddParameter(cmd, "client_id", clientId);
|
||||
AddParameter(cmd, "limit", limit);
|
||||
AddParameter(cmd, "offset", offset);
|
||||
},
|
||||
mapRow: MapToken,
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<OidcTokenEntity>> ListByScopeAsync(string tenant, string scope, DateTimeOffset? issuedAfter, int limit, CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = """
|
||||
SELECT id, token_id, subject_id, client_id, token_type, reference_id, created_at, expires_at, redeemed_at, payload, properties
|
||||
FROM authority.oidc_tokens
|
||||
WHERE (properties->>'tenant') = @tenant
|
||||
AND position(' ' || @scope || ' ' IN ' ' || COALESCE(properties->>'scope', '') || ' ') > 0
|
||||
AND (@issued_after IS NULL OR created_at >= @issued_after)
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT @limit
|
||||
""";
|
||||
|
||||
return await QueryAsync(
|
||||
tenantId: string.Empty,
|
||||
sql: sql,
|
||||
configureCommand: cmd =>
|
||||
{
|
||||
AddParameter(cmd, "tenant", tenant);
|
||||
AddParameter(cmd, "scope", scope);
|
||||
AddParameter(cmd, "issued_after", issuedAfter);
|
||||
AddParameter(cmd, "limit", limit);
|
||||
},
|
||||
mapRow: MapToken,
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<OidcTokenEntity>> ListRevokedAsync(string? tenant, int limit, CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = """
|
||||
SELECT id, token_id, subject_id, client_id, token_type, reference_id, created_at, expires_at, redeemed_at, payload, properties
|
||||
FROM authority.oidc_tokens
|
||||
WHERE lower(COALESCE(properties->>'status', 'valid')) = 'revoked'
|
||||
AND (@tenant IS NULL OR (properties->>'tenant') = @tenant)
|
||||
ORDER BY token_id ASC, id ASC
|
||||
LIMIT @limit
|
||||
""";
|
||||
|
||||
return await QueryAsync(
|
||||
tenantId: string.Empty,
|
||||
sql: sql,
|
||||
configureCommand: cmd =>
|
||||
{
|
||||
AddParameter(cmd, "tenant", tenant);
|
||||
AddParameter(cmd, "limit", limit);
|
||||
},
|
||||
mapRow: MapToken,
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<long> CountActiveDelegationTokensAsync(string tenant, string? serviceAccountId, DateTimeOffset now, CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = """
|
||||
SELECT COUNT(*)
|
||||
FROM authority.oidc_tokens
|
||||
WHERE (properties->>'tenant') = @tenant
|
||||
AND (@service_account_id IS NULL OR (properties->>'service_account_id') = @service_account_id)
|
||||
AND lower(COALESCE(properties->>'status', 'valid')) <> 'revoked'
|
||||
AND (expires_at IS NULL OR expires_at > @now)
|
||||
""";
|
||||
|
||||
var count = await ExecuteScalarAsync<long>(
|
||||
tenantId: string.Empty,
|
||||
sql: sql,
|
||||
configureCommand: cmd =>
|
||||
{
|
||||
AddParameter(cmd, "tenant", tenant);
|
||||
AddParameter(cmd, "service_account_id", serviceAccountId);
|
||||
AddParameter(cmd, "now", now);
|
||||
},
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return count ?? 0;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<OidcTokenEntity>> ListActiveDelegationTokensAsync(string tenant, string? serviceAccountId, DateTimeOffset now, int limit, CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = """
|
||||
SELECT id, token_id, subject_id, client_id, token_type, reference_id, created_at, expires_at, redeemed_at, payload, properties
|
||||
FROM authority.oidc_tokens
|
||||
WHERE (properties->>'tenant') = @tenant
|
||||
AND (@service_account_id IS NULL OR (properties->>'service_account_id') = @service_account_id)
|
||||
AND lower(COALESCE(properties->>'status', 'valid')) <> 'revoked'
|
||||
AND (expires_at IS NULL OR expires_at > @now)
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT @limit
|
||||
""";
|
||||
|
||||
return await QueryAsync(
|
||||
tenantId: string.Empty,
|
||||
sql: sql,
|
||||
configureCommand: cmd =>
|
||||
{
|
||||
AddParameter(cmd, "tenant", tenant);
|
||||
AddParameter(cmd, "service_account_id", serviceAccountId);
|
||||
AddParameter(cmd, "now", now);
|
||||
AddParameter(cmd, "limit", limit);
|
||||
},
|
||||
mapRow: MapToken,
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<OidcTokenEntity>> ListAsync(int limit, CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = """
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
/// <summary>
|
||||
/// PostgreSQL repository for revocations.
|
||||
/// </summary>
|
||||
public sealed class RevocationRepository : RepositoryBase<AuthorityDataSource>
|
||||
public sealed class RevocationRepository : RepositoryBase<AuthorityDataSource>, IRevocationRepository
|
||||
{
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.General);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace StellaOps.Authority.Persistence.Postgres.Repositories;
|
||||
/// <summary>
|
||||
/// PostgreSQL repository for service accounts.
|
||||
/// </summary>
|
||||
public sealed class ServiceAccountRepository : RepositoryBase<AuthorityDataSource>
|
||||
public sealed class ServiceAccountRepository : RepositoryBase<AuthorityDataSource>, IServiceAccountRepository
|
||||
{
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.General);
|
||||
|
||||
|
||||
@@ -79,5 +79,13 @@ public static class ServiceCollectionExtensions
|
||||
services.AddScoped<IOfflineKitAuditRepository>(sp => sp.GetRequiredService<OfflineKitAuditRepository>());
|
||||
services.AddScoped<IOfflineKitAuditEmitter, OfflineKitAuditEmitter>();
|
||||
services.AddScoped<RevocationExportStateRepository>();
|
||||
|
||||
services.AddScoped<IBootstrapInviteRepository>(sp => sp.GetRequiredService<BootstrapInviteRepository>());
|
||||
services.AddScoped<IServiceAccountRepository>(sp => sp.GetRequiredService<ServiceAccountRepository>());
|
||||
services.AddScoped<IClientRepository>(sp => sp.GetRequiredService<ClientRepository>());
|
||||
services.AddScoped<IRevocationRepository>(sp => sp.GetRequiredService<RevocationRepository>());
|
||||
services.AddScoped<ILoginAttemptRepository>(sp => sp.GetRequiredService<LoginAttemptRepository>());
|
||||
services.AddScoped<IOidcTokenRepository>(sp => sp.GetRequiredService<OidcTokenRepository>());
|
||||
services.AddScoped<IAirgapAuditRepository>(sp => sp.GetRequiredService<AirgapAuditRepository>());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user