Public Access
Add data model, DbContext and initial migration (M1)
Schema for identity, vaults, grants, hosts and the sync change log, verified against a real PostgreSQL 18 container rather than an in-memory provider: partial unique indexes, CHECK constraints, citext and identity-always columns are all provider behaviour that an in-memory fake would not exercise. Invariants pushed into the database, so they hold even when application code has a bug: - ck_host_relay_target is a security boundary, not tidiness. A host may carry a plaintext hostname and port ONLY when relay is deliberately enabled. Both directions are tested; the important one is that relay-disabled hosts cannot carry an address, since otherwise a bug would silently give the server infrastructure visibility it was never granted. - ck_vault_owner: exactly one of owner_user_id or team_id, or permission resolution would have no defined answer. - ck_vault_key_grant_recipient: member grants name a user; recovery and escrow grants are wrapped to a key and must not. - ck_user_key_wrap_kdf: a password-derived wrap without its parameters is permanently unopenable, so a partial write is rejected outright. Present from the first migration on purpose: - GrantKind (Member/Recovery/Escrow). Recovery cannot be bolted on later — every vault created before it existed would be unrecoverable by design. - team and team_membership, though team features are M3. Adding them later would mean introducing a foreign key on a live vault table. - Host.ContentKeyId, reserved for per-item content keys wrapped to individual users. - user_key as its own table, so key rotation does not require altering the user row. Two things verified rather than assumed: - Npgsql's UseXminAsConcurrencyToken helper no longer exists in EF 10, so xmin is mapped directly in XminConcurrency. The generated migration *looks* like it creates an xmin column; it does not. Confirmed by inspecting pg_attribute (attnum -2, a system column) and by grepping the emitted DDL. A test pins both, because had it created a real column PostgreSQL would have rejected the name. - EF Core is now pinned centrally. The Npgsql provider asks for 10.0.4 while EntityFrameworkCore.Design pulls 10.0.10, and because Design is PrivateAssets=all that higher version does not flow to referencing projects — producing a CS1705 in any test project referencing Infrastructure. Also commits artifacts/schema/v0.1.sql, the idempotent script, as the baseline for future upgrade tests. Verified: 0 warnings, 122 tests pass (27 new against Postgres), format clean.
This commit is contained in:
1 parent
06d04b490b
commit
eaf68c86b0
24 files changed
+5843
No files matched your search
@@ -0,0 +1,968 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using DodoSSH.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DodoSSH.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(DodoDbContext))]
|
||||
partial class DodoDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasDefaultSchema("dodo")
|
||||
.HasAnnotation("ProductVersion", "10.0.10")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "citext");
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.Device", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTimeOffset>("EnrolledAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("enrolled_at_utc");
|
||||
|
||||
b.Property<DateTimeOffset?>("LastSeenAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("last_seen_at_utc");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("platform");
|
||||
|
||||
b.Property<byte[]>("PublicKey")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("public_key");
|
||||
|
||||
b.Property<DateTimeOffset?>("RevokedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("revoked_at_utc");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_device");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_device_user_id");
|
||||
|
||||
b.ToTable("device", "dodo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.Host", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<long>("ChangeSequence")
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("change_sequence");
|
||||
|
||||
b.Property<Guid?>("ContentKeyId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("content_key_id");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at_utc");
|
||||
|
||||
b.Property<Guid>("CreatedByUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("created_by_user_id");
|
||||
|
||||
b.Property<byte[]>("DataKeyWrap")
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("data_key_wrap");
|
||||
|
||||
b.Property<DateTimeOffset?>("DeletedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("deleted_at_utc");
|
||||
|
||||
b.Property<Guid?>("GroupId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("group_id");
|
||||
|
||||
b.Property<string>("Hostname")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)")
|
||||
.HasColumnName("hostname");
|
||||
|
||||
b.Property<int>("KeyGeneration")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("key_generation");
|
||||
|
||||
b.Property<byte[]>("Payload")
|
||||
.IsRequired()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("payload");
|
||||
|
||||
b.Property<short>("PayloadAadVersion")
|
||||
.HasColumnType("smallint")
|
||||
.HasColumnName("payload_aad_version");
|
||||
|
||||
b.Property<int?>("Port")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("port");
|
||||
|
||||
b.Property<bool>("RelayEnabled")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("relay_enabled");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at_utc");
|
||||
|
||||
b.Property<Guid>("UpdatedByUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("updated_by_user_id");
|
||||
|
||||
b.Property<Guid>("VaultId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("vault_id");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("version");
|
||||
|
||||
b.Property<uint>("xmin")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("xid")
|
||||
.HasColumnName("xmin");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_host");
|
||||
|
||||
b.HasIndex("VaultId")
|
||||
.HasDatabaseName("ix_host_vault_live")
|
||||
.HasFilter("deleted_at_utc IS NULL");
|
||||
|
||||
b.HasIndex("VaultId", "ChangeSequence")
|
||||
.HasDatabaseName("ix_host_vault_id_change_sequence");
|
||||
|
||||
b.ToTable("host", "dodo", t =>
|
||||
{
|
||||
t.HasCheckConstraint("ck_host_port_range", "port IS NULL OR (port BETWEEN 1 AND 65535)");
|
||||
|
||||
t.HasCheckConstraint("ck_host_relay_target", "(relay_enabled AND hostname IS NOT NULL AND port IS NOT NULL)\nOR (NOT relay_enabled AND hostname IS NULL AND port IS NULL)");
|
||||
|
||||
t.HasCheckConstraint("ck_host_version", "version >= 1");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.KeyLogEntry", b =>
|
||||
{
|
||||
b.Property<long>("Sequence")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("sequence");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property<long>("Sequence"));
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at_utc");
|
||||
|
||||
b.Property<byte[]>("EncryptionPublicKey")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("encryption_public_key");
|
||||
|
||||
b.Property<int>("Generation")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("generation");
|
||||
|
||||
b.Property<byte[]>("Hash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("hash");
|
||||
|
||||
b.Property<byte[]>("PreviousHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("previous_hash");
|
||||
|
||||
b.Property<byte[]>("SigningPublicKey")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("signing_public_key");
|
||||
|
||||
b.Property<byte[]>("StatementSignature")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("statement_signature");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Sequence")
|
||||
.HasName("pk_key_log");
|
||||
|
||||
b.HasIndex("Hash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_key_log_hash");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_key_log_user_id");
|
||||
|
||||
b.ToTable("key_log", "dodo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.SyncChange", b =>
|
||||
{
|
||||
b.Property<long>("Sequence")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("sequence");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property<long>("Sequence"));
|
||||
|
||||
b.Property<Guid>("ActorUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("actor_user_id");
|
||||
|
||||
b.Property<Guid>("EntityId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("entity_id");
|
||||
|
||||
b.Property<int>("EntityType")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("entity_type");
|
||||
|
||||
b.Property<DateTimeOffset>("OccurredAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("occurred_at_utc");
|
||||
|
||||
b.Property<int>("Operation")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("operation");
|
||||
|
||||
b.Property<int>("Revision")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("revision");
|
||||
|
||||
b.Property<Guid>("VaultId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("vault_id");
|
||||
|
||||
b.HasKey("Sequence")
|
||||
.HasName("pk_sync_change");
|
||||
|
||||
b.HasIndex("VaultId", "Sequence")
|
||||
.HasDatabaseName("ix_sync_change_vault_id_sequence");
|
||||
|
||||
b.HasIndex("VaultId", "EntityId", "Sequence")
|
||||
.IsDescending(false, false, true)
|
||||
.HasDatabaseName("ix_sync_change_vault_id_entity_id_sequence");
|
||||
|
||||
b.ToTable("sync_change", "dodo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.SyncOperationReceipt", b =>
|
||||
{
|
||||
b.Property<Guid>("OperationId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("operation_id");
|
||||
|
||||
b.Property<long?>("AppliedChangeSequence")
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("applied_change_sequence");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at_utc");
|
||||
|
||||
b.Property<int?>("ResultVersion")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("result_version");
|
||||
|
||||
b.Property<Guid>("VaultId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("vault_id");
|
||||
|
||||
b.HasKey("OperationId")
|
||||
.HasName("pk_sync_operation_receipt");
|
||||
|
||||
b.HasIndex("VaultId", "CreatedAtUtc")
|
||||
.HasDatabaseName("ix_sync_operation_receipt_vault_id_created_at_utc");
|
||||
|
||||
b.ToTable("sync_operation_receipt", "dodo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.Team", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at_utc");
|
||||
|
||||
b.Property<Guid>("CreatedByUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("created_by_user_id");
|
||||
|
||||
b.Property<DateTimeOffset?>("DeletedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("deleted_at_utc");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("citext")
|
||||
.HasColumnName("slug");
|
||||
|
||||
b.Property<uint>("xmin")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("xid")
|
||||
.HasColumnName("xmin");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_team");
|
||||
|
||||
b.HasIndex("Slug")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_team_slug")
|
||||
.HasFilter("deleted_at_utc IS NULL");
|
||||
|
||||
b.ToTable("team", "dodo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.TeamMembership", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at_utc");
|
||||
|
||||
b.Property<DateTimeOffset?>("DeletedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("deleted_at_utc");
|
||||
|
||||
b.Property<Guid?>("InvitedByUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("invited_by_user_id");
|
||||
|
||||
b.Property<DateTimeOffset?>("JoinedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("joined_at_utc");
|
||||
|
||||
b.Property<int>("Role")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("role");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<Guid>("TeamId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("team_id");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.Property<uint>("xmin")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("xid")
|
||||
.HasColumnName("xmin");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_team_membership");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_team_membership_user_id");
|
||||
|
||||
b.HasIndex("TeamId", "UserId")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_team_membership_team_id_user_id")
|
||||
.HasFilter("deleted_at_utc IS NULL");
|
||||
|
||||
b.ToTable("team_membership", "dodo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.UserAccount", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at_utc");
|
||||
|
||||
b.Property<DateTimeOffset?>("DeletedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("deleted_at_utc");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasColumnName("display_name");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(320)
|
||||
.HasColumnType("citext")
|
||||
.HasColumnName("email");
|
||||
|
||||
b.Property<DateTimeOffset?>("EnrolledAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("enrolled_at_utc");
|
||||
|
||||
b.Property<string>("Issuer")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)")
|
||||
.HasColumnName("issuer");
|
||||
|
||||
b.Property<DateTimeOffset?>("LastSeenAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("last_seen_at_utc");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<string>("Subject")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasColumnName("subject");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at_utc");
|
||||
|
||||
b.Property<uint>("xmin")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("xid")
|
||||
.HasColumnName("xmin");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_user_account");
|
||||
|
||||
b.HasIndex("Email")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_user_account_email")
|
||||
.HasFilter("email IS NOT NULL AND deleted_at_utc IS NULL");
|
||||
|
||||
b.HasIndex("Issuer", "Subject")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_user_account_issuer_subject");
|
||||
|
||||
b.ToTable("user_account", "dodo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.UserKey", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at_utc");
|
||||
|
||||
b.Property<byte[]>("EncryptionPublicKey")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("encryption_public_key");
|
||||
|
||||
b.Property<byte[]>("FingerprintSha256")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("fingerprint_sha256");
|
||||
|
||||
b.Property<int>("Generation")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("generation");
|
||||
|
||||
b.Property<string>("IdentityProviderBinding")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("identity_provider_binding");
|
||||
|
||||
b.Property<bool>("IsCurrent")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_current");
|
||||
|
||||
b.Property<DateTimeOffset?>("RevokedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("revoked_at_utc");
|
||||
|
||||
b.Property<byte[]>("SigningPublicKey")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("signing_public_key");
|
||||
|
||||
b.Property<string>("Statement")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("statement");
|
||||
|
||||
b.Property<byte[]>("StatementSignature")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("statement_signature");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_user_key");
|
||||
|
||||
b.HasIndex("FingerprintSha256")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_user_key_fingerprint_sha256");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_user_key_current")
|
||||
.HasFilter("is_current");
|
||||
|
||||
b.HasIndex("UserId", "Generation")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_user_key_user_id_generation");
|
||||
|
||||
b.ToTable("user_key", "dodo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.UserKeyWrap", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at_utc");
|
||||
|
||||
b.Property<Guid?>("DeviceId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("device_id");
|
||||
|
||||
b.Property<string>("KdfAlgorithm")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("kdf_algorithm");
|
||||
|
||||
b.Property<int?>("KdfMemoryKibibytes")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("kdf_memory_kibibytes");
|
||||
|
||||
b.Property<int?>("KdfParallelism")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("kdf_parallelism");
|
||||
|
||||
b.Property<int?>("KdfPasses")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("kdf_passes");
|
||||
|
||||
b.Property<byte[]>("KdfSalt")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("kdf_salt");
|
||||
|
||||
b.Property<int>("Kind")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("kind");
|
||||
|
||||
b.Property<DateTimeOffset?>("LastUsedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("last_used_at_utc");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.Property<byte[]>("Wrap")
|
||||
.IsRequired()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("wrap");
|
||||
|
||||
b.Property<int>("WrapVersion")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("wrap_version");
|
||||
|
||||
b.Property<uint>("xmin")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("xid")
|
||||
.HasColumnName("xmin");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_user_key_wrap");
|
||||
|
||||
b.HasIndex("DeviceId")
|
||||
.HasDatabaseName("ix_user_key_wrap_device_id");
|
||||
|
||||
b.HasIndex("UserId", "DeviceId")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_user_key_wrap_user_device")
|
||||
.HasFilter("device_id IS NOT NULL");
|
||||
|
||||
b.HasIndex("UserId", "Kind")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_user_key_wrap_user_kind")
|
||||
.HasFilter("device_id IS NULL");
|
||||
|
||||
b.ToTable("user_key_wrap", "dodo", t =>
|
||||
{
|
||||
t.HasCheckConstraint("ck_user_key_wrap_device", "(kind = 2 AND device_id IS NOT NULL) OR (kind <> 2 AND device_id IS NULL)");
|
||||
|
||||
t.HasCheckConstraint("ck_user_key_wrap_kdf", "(kind IN (1, 3) AND kdf_algorithm IS NOT NULL AND kdf_salt IS NOT NULL\n AND kdf_memory_kibibytes IS NOT NULL AND kdf_passes IS NOT NULL\n AND kdf_parallelism IS NOT NULL)\nOR (kind IN (2, 4) AND kdf_algorithm IS NULL AND kdf_salt IS NULL)");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.Vault", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at_utc");
|
||||
|
||||
b.Property<DateTimeOffset?>("DeletedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("deleted_at_utc");
|
||||
|
||||
b.Property<int>("KeyGeneration")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("key_generation");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<int>("OwnerKind")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("owner_kind");
|
||||
|
||||
b.Property<Guid?>("OwnerUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("owner_user_id");
|
||||
|
||||
b.Property<int>("RekeyReason")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("rekey_reason");
|
||||
|
||||
b.Property<bool>("RekeyRequired")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("rekey_required");
|
||||
|
||||
b.Property<Guid?>("TeamId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("team_id");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at_utc");
|
||||
|
||||
b.Property<uint>("xmin")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("xid")
|
||||
.HasColumnName("xmin");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_vault");
|
||||
|
||||
b.HasIndex("OwnerUserId")
|
||||
.HasDatabaseName("ix_vault_owner_user_id");
|
||||
|
||||
b.HasIndex("TeamId")
|
||||
.HasDatabaseName("ix_vault_team_id");
|
||||
|
||||
b.ToTable("vault", "dodo", t =>
|
||||
{
|
||||
t.HasCheckConstraint("ck_vault_key_generation", "key_generation >= 1");
|
||||
|
||||
t.HasCheckConstraint("ck_vault_owner", "(owner_kind = 1 AND owner_user_id IS NOT NULL AND team_id IS NULL)\nOR (owner_kind = 2 AND team_id IS NOT NULL AND owner_user_id IS NULL)");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.VaultKeyGrant", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at_utc");
|
||||
|
||||
b.Property<byte[]>("GranterKeyFingerprint")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("granter_key_fingerprint");
|
||||
|
||||
b.Property<Guid>("GranterUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("granter_user_id");
|
||||
|
||||
b.Property<int>("KeyGeneration")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("key_generation");
|
||||
|
||||
b.Property<byte[]>("KeyLogHead")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("key_log_head");
|
||||
|
||||
b.Property<int>("Kind")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("kind");
|
||||
|
||||
b.Property<byte[]>("RecipientKeyFingerprint")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("recipient_key_fingerprint");
|
||||
|
||||
b.Property<Guid?>("RecipientUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("recipient_user_id");
|
||||
|
||||
b.Property<DateTimeOffset?>("RevokedAtUtc")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("revoked_at_utc");
|
||||
|
||||
b.Property<byte[]>("Signature")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("signature");
|
||||
|
||||
b.Property<int>("State")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("state");
|
||||
|
||||
b.Property<Guid>("VaultId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("vault_id");
|
||||
|
||||
b.Property<byte[]>("WrappedKey")
|
||||
.IsRequired()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("wrapped_key");
|
||||
|
||||
b.Property<uint>("xmin")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("xid")
|
||||
.HasColumnName("xmin");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_vault_key_grant");
|
||||
|
||||
b.HasIndex("RecipientUserId")
|
||||
.HasDatabaseName("ix_vault_key_grant_recipient_user_id");
|
||||
|
||||
b.HasIndex("VaultId", "KeyGeneration", "RecipientUserId")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_vault_key_grant_vault_id_key_generation_recipient_user_id")
|
||||
.HasFilter("revoked_at_utc IS NULL AND recipient_user_id IS NOT NULL");
|
||||
|
||||
b.ToTable("vault_key_grant", "dodo", t =>
|
||||
{
|
||||
t.HasCheckConstraint("ck_vault_key_grant_recipient", "(kind = 1 AND recipient_user_id IS NOT NULL) OR (kind <> 1 AND recipient_user_id IS NULL)");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.Device", b =>
|
||||
{
|
||||
b.HasOne("DodoSSH.Domain.UserAccount", "User")
|
||||
.WithMany("Devices")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_device_users_user_id");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.Host", b =>
|
||||
{
|
||||
b.HasOne("DodoSSH.Domain.Vault", "Vault")
|
||||
.WithMany("Hosts")
|
||||
.HasForeignKey("VaultId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_host_vaults_vault_id");
|
||||
|
||||
b.Navigation("Vault");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.TeamMembership", b =>
|
||||
{
|
||||
b.HasOne("DodoSSH.Domain.Team", "Team")
|
||||
.WithMany("Memberships")
|
||||
.HasForeignKey("TeamId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_team_membership_team_team_id");
|
||||
|
||||
b.HasOne("DodoSSH.Domain.UserAccount", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_team_membership_users_user_id");
|
||||
|
||||
b.Navigation("Team");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.UserKey", b =>
|
||||
{
|
||||
b.HasOne("DodoSSH.Domain.UserAccount", "User")
|
||||
.WithMany("Keys")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_user_key_user_account_user_id");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.UserKeyWrap", b =>
|
||||
{
|
||||
b.HasOne("DodoSSH.Domain.Device", "Device")
|
||||
.WithMany()
|
||||
.HasForeignKey("DeviceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.HasConstraintName("fk_user_key_wrap_device_device_id");
|
||||
|
||||
b.HasOne("DodoSSH.Domain.UserAccount", "User")
|
||||
.WithMany("KeyWraps")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_user_key_wrap_user_account_user_id");
|
||||
|
||||
b.Navigation("Device");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.Vault", b =>
|
||||
{
|
||||
b.HasOne("DodoSSH.Domain.UserAccount", "OwnerUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("OwnerUserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.HasConstraintName("fk_vault_user_account_owner_user_id");
|
||||
|
||||
b.HasOne("DodoSSH.Domain.Team", "Team")
|
||||
.WithMany()
|
||||
.HasForeignKey("TeamId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.HasConstraintName("fk_vault_team_team_id");
|
||||
|
||||
b.Navigation("OwnerUser");
|
||||
|
||||
b.Navigation("Team");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.VaultKeyGrant", b =>
|
||||
{
|
||||
b.HasOne("DodoSSH.Domain.UserAccount", "RecipientUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("RecipientUserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.HasConstraintName("fk_vault_key_grant_user_account_recipient_user_id");
|
||||
|
||||
b.HasOne("DodoSSH.Domain.Vault", "Vault")
|
||||
.WithMany("KeyGrants")
|
||||
.HasForeignKey("VaultId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_vault_key_grant_vault_vault_id");
|
||||
|
||||
b.Navigation("RecipientUser");
|
||||
|
||||
b.Navigation("Vault");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.Team", b =>
|
||||
{
|
||||
b.Navigation("Memberships");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.UserAccount", b =>
|
||||
{
|
||||
b.Navigation("Devices");
|
||||
|
||||
b.Navigation("KeyWraps");
|
||||
|
||||
b.Navigation("Keys");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DodoSSH.Domain.Vault", b =>
|
||||
{
|
||||
b.Navigation("Hosts");
|
||||
|
||||
b.Navigation("KeyGrants");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user