🚧 Add CA and server cert management
This commit is contained in:
@@ -14,7 +14,12 @@ from app import models # noqa: F401 (register all models with Base)
|
||||
config = context.config
|
||||
|
||||
if config.config_file_name is not None:
|
||||
fileConfig(config.config_file_name)
|
||||
# Our minimal alembic.ini intentionally has no [loggers]/[handlers]/
|
||||
# [formatters] sections; fileConfig raises KeyError without them.
|
||||
try:
|
||||
fileConfig(config.config_file_name)
|
||||
except Exception: # noqa: BLE001 - logging config is optional here
|
||||
pass
|
||||
|
||||
url = get_settings().database_url
|
||||
if url.startswith("postgresql://"):
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
"""Release 2: expand certificates table for CA/server-certificate PKI.
|
||||
|
||||
Revision ID: r2_0002
|
||||
Revises: r1_0001
|
||||
Create Date: 2026-09-19
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
revision: str = "r2_0002"
|
||||
down_revision: Union[str, None] = "r1_0001"
|
||||
branch_labels: Union[str, Sequence[str] | None] = None
|
||||
depends_on: Union[str, Sequence[str] | None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# The certificates table exists since r1_0001 (forward-compatible
|
||||
# placeholder). Release 2 adds CA semantics, hierarchy, SANs,
|
||||
# serial/fingerprint identity, and a lifecycle status.
|
||||
op.add_column("certificates", sa.Column("is_ca", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
op.add_column("certificates", sa.Column("parent_id", sa.Uuid(), nullable=True))
|
||||
op.add_column("certificates", sa.Column("key_algorithm", sa.String(20), nullable=False, server_default="ecdsa-p256"))
|
||||
# Serial numbers are unique per RFC 5280; existing rows have none, so
|
||||
# the column is added nullable first, then made NOT NULL below (the
|
||||
# table has never been written to, so no backfill is needed).
|
||||
op.add_column("certificates", sa.Column("serial_number", sa.String(64), nullable=True))
|
||||
op.add_column("certificates", sa.Column("dns_sans", sa.JSON(), nullable=False, server_default="[]"))
|
||||
op.add_column("certificates", sa.Column("ip_sans", sa.JSON(), nullable=False, server_default="[]"))
|
||||
op.add_column("certificates", sa.Column("fingerprint", sa.String(200), nullable=True))
|
||||
op.add_column("certificates", sa.Column("status", sa.String(20), nullable=False, server_default="issued"))
|
||||
op.create_foreign_key(
|
||||
"fk_certificates_parent",
|
||||
"certificates",
|
||||
"certificates",
|
||||
["parent_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
# No rows exist yet in practice (table was never written to), so the
|
||||
# unique index is safe; if a legacy row existed without a serial it
|
||||
# must be removed manually first.
|
||||
op.create_index("ix_certificates_serial_number", "certificates", ["serial_number"], unique=True)
|
||||
op.create_index("ix_certificates_fingerprint", "certificates", ["fingerprint"])
|
||||
op.alter_column("certificates", "serial_number", nullable=False)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.alter_column("certificates", "serial_number", nullable=True)
|
||||
op.drop_index("ix_certificates_fingerprint", table_name="certificates")
|
||||
op.drop_index("ix_certificates_serial_number", table_name="certificates")
|
||||
op.drop_constraint("fk_certificates_parent", "certificates", type_="foreignkey")
|
||||
op.drop_column("certificates", "status")
|
||||
op.drop_column("certificates", "fingerprint")
|
||||
op.drop_column("certificates", "ip_sans")
|
||||
op.drop_column("certificates", "dns_sans")
|
||||
op.drop_column("certificates", "serial_number")
|
||||
op.drop_column("certificates", "key_algorithm")
|
||||
op.drop_column("certificates", "parent_id")
|
||||
op.drop_column("certificates", "is_ca")
|
||||
Reference in New Issue
Block a user