1c266523bc
The trust engine. New `verif` schema (5 tables) + a data-driven verification pipeline: steps are rows (6 seeded step-types), not a code enum. - nurse_verifications.status is the single source of verification truth; nurse_profiles.is_verified is flipped ONLY inside the finalize transaction (VerificationAggregator: tracked verification + tracked profile -> one commit) and reversed on suspension/expiry — no in-between state. - is_automated snapshotted onto each step at submit; steps seeded from active required step-types; automated runs (identity-KYC, Shahkar, IBAN ownership) find their step by code. - users.national_id populated only on identity-KYC pass; Shahkar + IBAN owner compare against it (money-mule guard); shared-SIM -> shared_sim support alert. - Documents are metadata-only behind signed URLs; credential_number encrypted and never serialized; public trust badge exposes credential TYPES, not numbers; holder-name cross-checked against the verified identity before recording. - Admin-triggered credential-expiry scan reverts lapsed steps, re-gates bookability, raises a verification_expired alert + verification_expiry_prompt notification (scheduled cron deferred; config key verification_expiry_scan_cadence_hours). Three new mock vendor seams (IShahkarVerifier / IIdentityKycProvider / ICredentialVerifier) behind DI; reuses b3 IBankAccountOwnershipVerifier and b0 IObjectStorage/IFieldEncryptor. 15 endpoints across 4 controllers. Two migrations (tables + step-type seed). 154 tests pass, zero new warnings. Contract dev/contracts/domains/verification.md + swagger snapshot refreshed; handoff/report/mocks-registry updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
39 lines
1.5 KiB
C#
39 lines
1.5 KiB
C#
using Baya.Domain.Entities.Identity;
|
|
using Baya.Domain.Entities.User;
|
|
using Baya.Domain.Entities.Verification;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
|
|
|
namespace Baya.Infrastructure.Persistence.Configuration.VerificationConfig;
|
|
|
|
internal sealed class NurseCredentialConfig : IEntityTypeConfiguration<NurseCredential>
|
|
{
|
|
public void Configure(EntityTypeBuilder<NurseCredential> builder)
|
|
{
|
|
builder.ToTable("NurseCredentials", "verif");
|
|
|
|
builder.Property(c => c.CredentialType).HasMaxLength(50).IsRequired();
|
|
// credential_number is encrypted at rest (converter wired in ApplicationDbContext) — never serialized.
|
|
builder.Property(c => c.CredentialNumber).HasMaxLength(256).IsRequired();
|
|
builder.Property(c => c.HolderNameSnapshot).HasMaxLength(200).IsRequired();
|
|
builder.Property(c => c.IssuingAuthority).HasMaxLength(200).IsRequired();
|
|
builder.Property(c => c.VerificationSource).HasMaxLength(300);
|
|
builder.Property(c => c.VerificationMethod).HasMaxLength(20).IsRequired();
|
|
|
|
// Badge + renewal reads: the nurse's non-expired credentials by type.
|
|
builder.HasIndex(c => new { c.NurseId, c.CredentialType });
|
|
|
|
builder.HasOne<NurseProfile>()
|
|
.WithMany()
|
|
.HasForeignKey(c => c.NurseId)
|
|
.IsRequired();
|
|
|
|
builder.HasOne<User>()
|
|
.WithMany()
|
|
.HasForeignKey(c => c.VerifiedByAdminId)
|
|
.IsRequired(false);
|
|
|
|
builder.HasQueryFilter(c => c.DeletedAt == null);
|
|
}
|
|
}
|