# Generated by Django 5.2.9 on 2025-12-09 06:22

from hashlib import md5

from cryptography.hazmat.backends import default_backend
from cryptography.x509 import load_pem_x509_certificate
from django.db import migrations, models

from authentik.crypto.signals import extract_certificate_metadata
from authentik.lib.migrations import progress_bar


def backfill_certificate_metadata(apps, schema_editor):  # noqa: ARG001
    """Backfill certificate metadata and kid for existing records."""

    db_alias = schema_editor.connection.alias
    CertificateKeyPair = apps.get_model("authentik_crypto", "CertificateKeyPair")

    print("\nStoring extra data about certificates, this might take a couple of minutes...")
    for cert in progress_bar(CertificateKeyPair.objects.using(db_alias).all()):
        updated_fields = []

        if cert.certificate_data:
            try:
                certificate = load_pem_x509_certificate(
                    cert.certificate_data.encode("utf-8"), default_backend()
                )
                metadata = extract_certificate_metadata(certificate)

                cert.key_type = metadata["key_type"]
                cert.cert_expiry = metadata["cert_expiry"]
                cert.cert_subject = metadata["cert_subject"]
                cert.fingerprint_sha256 = metadata["fingerprint_sha256"]
                cert.fingerprint_sha1 = metadata["fingerprint_sha1"]
                updated_fields.extend(
                    [
                        "key_type",
                        "cert_expiry",
                        "cert_subject",
                        "fingerprint_sha256",
                        "fingerprint_sha1",
                    ]
                )
            except ValueError, TypeError, AttributeError:
                pass

        # Backfill kid with MD5 for backwards compatibility
        if cert.key_data:
            cert.kid = md5(cert.key_data.encode("utf-8"), usedforsecurity=False).hexdigest()
            updated_fields.append("kid")

        if updated_fields:
            cert.save(update_fields=updated_fields, using=db_alias)


class Migration(migrations.Migration):

    dependencies = [
        ("authentik_crypto", "0005_alter_certificatekeypair_options"),
    ]

    operations = [
        migrations.AddField(
            model_name="certificatekeypair",
            name="cert_expiry",
            field=models.DateTimeField(blank=True, help_text="Certificate expiry date", null=True),
        ),
        migrations.AddField(
            model_name="certificatekeypair",
            name="cert_subject",
            field=models.TextField(
                blank=True, help_text="Certificate subject as RFC4514 string", null=True
            ),
        ),
        migrations.AddField(
            model_name="certificatekeypair",
            name="fingerprint_sha1",
            field=models.CharField(
                blank=True,
                help_text="SHA1 fingerprint of the certificate",
                max_length=59,
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="certificatekeypair",
            name="fingerprint_sha256",
            field=models.CharField(
                blank=True,
                help_text="SHA256 fingerprint of the certificate",
                max_length=95,
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="certificatekeypair",
            name="key_type",
            field=models.CharField(
                blank=True,
                choices=[
                    ("rsa", "RSA"),
                    ("ec", "Elliptic Curve"),
                    ("dsa", "DSA"),
                    ("ed25519", "Ed25519"),
                    ("ed448", "Ed448"),
                ],
                help_text="Key algorithm type detected from the certificate's public key",
                max_length=16,
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="certificatekeypair",
            name="kid",
            field=models.CharField(
                blank=True, help_text="Key ID generated from private key", max_length=128, null=True
            ),
        ),
        migrations.RunPython(backfill_certificate_metadata, migrations.RunPython.noop),
    ]
