package certs

import (
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"math/big"
	"os"
	"testing"

	"github.com/stretchr/testify/require"

	"isc.org/stork/pki"
	dbmodel "isc.org/stork/server/database/model"
	dbtest "isc.org/stork/server/database/test"
	"isc.org/stork/testutil"
)

// Check if GenerateServerToken works.
func TestGenerateServerToken(t *testing.T) {
	db, _, teardown := dbtest.SetupDatabaseTestCase(t)
	defer teardown()

	token, err := GenerateServerToken(db)
	require.NoError(t, err)
	require.NotEmpty(t, token)

	token2, err := dbmodel.GetSecret(db, dbmodel.SecretServerToken)
	require.NoError(t, err)
	require.EqualValues(t, token, token2)
}

// Check if SetupServerCerts works.
func TestSetupServerCerts(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}

	db, _, teardown := dbtest.SetupDatabaseTestCase(t)
	defer teardown()

	// setup server certs for the first time - they should be generated
	rootCertPEM, serverCertPEM, serverKeyPEM, err := SetupServerCerts(db)
	require.NoError(t, err)
	require.NotEmpty(t, rootCertPEM)
	require.NotEmpty(t, serverCertPEM)
	require.NotEmpty(t, serverKeyPEM)

	// setup server certs for the second time - they should be taken from database
	rootCertPEM2, serverCertPEM2, serverKeyPEM2, err := SetupServerCerts(db)
	require.NoError(t, err)
	require.EqualValues(t, rootCertPEM, rootCertPEM2)
	require.EqualValues(t, serverCertPEM, serverCertPEM2)
	require.EqualValues(t, serverKeyPEM, serverKeyPEM2)

	// destroy some certs and check if all is recreated
	secret := &dbmodel.Secret{}
	_, err = db.Model(secret).Where("name != ?", "").Delete()
	require.NoError(t, err)

	rootCertPEM3, serverCertPEM3, serverKeyPEM3, err := SetupServerCerts(db)
	require.NoError(t, err)
	require.NotEqualValues(t, rootCertPEM, rootCertPEM3)
	require.NotEqualValues(t, serverCertPEM, serverCertPEM3)
	require.NotEqualValues(t, serverKeyPEM, serverKeyPEM3)

	// the CA cert that doesn't meet the requirements and server cert generated
	// using it should be regenerated

	// The below certs were generated by Stork server 1.15.0.
	secret = &dbmodel.Secret{Name: "cakey", Content: `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgaAeYgjhPH6bP3/CD
SlljVNyj2A+fgDWe0p3DUqJ+K1ShRANCAARhMooyi9raTxT8XDctdTJ9Cwej3w8D
hmM9a40IdfUilZkw8SVkyv3xFsqyfUqZJXiHgv1xWe+4akSwU9oyEAPQ
-----END PRIVATE KEY-----`}
	_, err = db.Model(secret).Where("name = ?", secret.Name).Update()
	require.NoError(t, err)

	rootCAPEMDeprecated := `-----BEGIN CERTIFICATE-----
MIIBjDCCATKgAwIBAgIBATAKBggqhkjOPQQDAjAzMQswCQYDVQQGEwJVUzESMBAG
A1UEChMJSVNDIFN0b3JrMRAwDgYDVQQDEwdSb290IENBMCAXDTI0MDIyNzEwMTg1
M1oYDzIwNTQwMjI3MTAxODUzWjAzMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSVND
IFN0b3JrMRAwDgYDVQQDEwdSb290IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
QgAEYTKKMova2k8U/Fw3LXUyfQsHo98PA4ZjPWuNCHX1IpWZMPElZMr98RbKsn1K
mSV4h4L9cVnvuGpEsFPaMhAD0KM1MDMwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNV
HQ4EFgQUXw7x4PTlkZYzQzRVs1SGMOFtkKMwCgYIKoZIzj0EAwIDSAAwRQIhAOvi
XECOQlYkyBtFlnddRhrzyByOMJppAaW7AimlfsqNAiA57U0UdW/KHcY5Prir3U2D
FEJ2P0UrBojtYWBtEE6e6Q==
-----END CERTIFICATE-----`
	secret = &dbmodel.Secret{Name: "cacert", Content: rootCAPEMDeprecated}
	_, err = db.Model(secret).Where("name = ?", secret.Name).Update()
	require.NoError(t, err)

	serverKeyPEMDeprecated := `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg+Of4J5OCfSNhj4N/
u05R/2otoJVtYsQAzou/JxpT4sihRANCAAQ2QJhvQoOCNIQP8oxu1ECHikmEaMP0
oqY4+sno13+mkL87k20aNx5RTLWVG+QRwd88MWqxmbuWD7m2pb2SXc1R
-----END PRIVATE KEY-----`
	secret = &dbmodel.Secret{Name: "srvkey", Content: serverKeyPEMDeprecated}
	_, err = db.Model(secret).Where("name = ?", secret.Name).Update()
	require.NoError(t, err)

	serverCertPEMDeprecated := `-----BEGIN CERTIFICATE-----
MIIEIDCCA8agAwIBAgIBAjAKBggqhkjOPQQDAjAzMQswCQYDVQQGEwJVUzESMBAG
A1UEChMJSVNDIFN0b3JrMRAwDgYDVQQDEwdSb290IENBMCAXDTI0MDIyNzEwMTg1
NloYDzIwNTQwMjI3MTAxODU2WjBGMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSVND
IFN0b3JrMQ8wDQYDVQQLEwZzZXJ2ZXIxEjAQBgNVBAMTCWxvY2FsaG9zdDBZMBMG
ByqGSM49AgEGCCqGSM49AwEHA0IABDZAmG9Cg4I0hA/yjG7UQIeKSYRow/Sipjj6
yejXf6aQvzuTbRo3HlFMtZUb5BHB3zwxarGZu5YPubalvZJdzVGjggK0MIICsDAf
BgNVHSMEGDAWgBRfDvHg9OWRljNDNFWzVIYw4W2QozCCAosGA1UdEQSCAoIwggJ+
gglsb2NhbGhvc3SCBGRlZXCCBGRlZXCCITE3Mi0xMDEtMDAwLTAwMS5yZXMuc3Bl
Y3RydW0uY29tLoIhMTcyLTEwMC0wMDAtMDAxLnJlcy5zcGVjdHJ1bS5jb20uggRk
ZWVwggRkZWVwgh4xNzItMTAzLTAtMS1keW5hbWljLm1pZGNvLm5ldC6CBGRlZXCC
BGRlZXCCBGRlZXCCDWlwNi1sb2NhbGhvc3SCBGRlZXCCBGRlZXCCBGRlZXCCBGRl
ZXCCBGRlZXCCBGRlZXCCBGRlZXCCBGRlZXCCBGRlZXCCBGRlZXCCBGRlZXCCBGRl
ZXCCBGRlZXCCBGRlZXCCBGRlZXCCBGRlZXCHBH8AAAGHBMCoAESHBKwdAAGHBKxl
AAGHBKxkAAGHBKwRAAGHBKwYAAGHBKxnAAGHBKxmAAGHBKwcAAGHBKwbAAGHEAAA
AAAAAAAAAAAAAAAAAAGHEP6AAAAAAAAAR2zKpk9tzgKHEDAADbgAAQAAAAAAAAAA
AAGHEP6AAAAAAAAAAAAAAAAAAAGHEP6AAAAAAAAAAELV//7pISeHEDAJDbgAAQAA
AAAAAAAAAAGHEP6AAAAAAAAAAEJt//7BdhiHEP6AAAAAAAAAAAAAAAAAAAGHEP6A
AAAAAAAAAEI7//6URWqHEDABDbgAAQAAAAAAAAAAAAGHEP6AAAAAAAAAAAAAAAAA
AAGHEP6AAAAAAAAA/Li9//5ctFGHEP6AAAAAAAAAzD0M//4iyZeHEP6AAAAAAAAA
DFgh//5FLv2HEP6AAAAAAAAAyD17//4iBpGHEP6AAAAAAAAAcBSG//7fQ+WHEP6A
AAAAAAAAePdX//7+yRkwCgYIKoZIzj0EAwIDSAAwRQIhALhJGMkwOhd7/vg+fc2F
JXrP7K24TYa3+fReArYUxtxOAiB5VINXs/Rh4UVOUG9FfAAM8cK7zIQRpgiWEf7J
uqJY6w==
-----END CERTIFICATE-----`
	secret = &dbmodel.Secret{Name: "srvcert", Content: serverCertPEMDeprecated}
	_, err = db.Model(secret).Where("name = ?", secret.Name).Update()
	require.NoError(t, err)

	rootCertPEM4, serverCertPEM4, serverKeyPEM4, err := SetupServerCerts(db)
	require.NoError(t, err)
	require.NotEqualValues(t, rootCertPEM3, rootCertPEM4)
	require.NotEqualValues(t, serverCertPEM3, serverCertPEM4)
	require.NotEqualValues(t, serverKeyPEM3, serverKeyPEM4)
	require.NotEqualValues(t, rootCertPEM4, rootCAPEMDeprecated)
	require.NotEqualValues(t, serverCertPEM4, serverCertPEMDeprecated)
	require.NotEqualValues(t, serverKeyPEM4, serverKeyPEMDeprecated)

	// Restore the server cert that was signed by another CA cert. It should
	// be regenerated.
	secret = &dbmodel.Secret{Name: "srvcert", Content: string(serverCertPEM3)}
	_, err = db.Model(secret).Where("name = ?", secret.Name).Update()
	require.NoError(t, err)

	secret = &dbmodel.Secret{Name: "srvkey", Content: string(serverKeyPEM3)}
	_, err = db.Model(secret).Where("name = ?", secret.Name).Update()
	require.NoError(t, err)

	rootCertPEM5, serverCertPEM5, serverKeyPEM5, err := SetupServerCerts(db)
	require.NoError(t, err)
	// It should preserve the CA root cert.
	require.EqualValues(t, rootCertPEM4, rootCertPEM5)
	require.NotEqualValues(t, serverCertPEM4, serverCertPEM5)
	require.NotEqualValues(t, serverKeyPEM4, serverKeyPEM5)
	require.NotEqualValues(t, rootCertPEM5, rootCertPEM3)
	require.NotEqualValues(t, serverCertPEM5, serverCertPEM3)
	require.NotEqualValues(t, serverKeyPEM5, serverKeyPEM3)

	// The server cert generated by an external CA cannot be regenerated.
	serverKeyPEM6, err := pki.GenKey()
	require.NoError(t, err)
	privateKey, err := pki.ParsePrivateKey(serverKeyPEM6)
	require.NoError(t, err)

	template := &x509.Certificate{
		Subject: pkix.Name{
			Organization: []string{"Foobar"},
		},
		SerialNumber: big.NewInt(42),
	}
	certBytes, err := x509.CreateCertificate(rand.Reader, template, template,
		&privateKey.PublicKey, privateKey)
	require.NoError(t, err)
	block := pem.Block{Type: "CERTIFICATE", Bytes: certBytes}
	serverCertPEM6 := pem.EncodeToMemory(&block)

	secret = &dbmodel.Secret{Name: "srvcert", Content: string(serverCertPEM6)}
	_, err = db.Model(secret).Where("name = ?", secret.Name).Update()
	require.NoError(t, err)

	secret = &dbmodel.Secret{Name: "srvkey", Content: string(serverKeyPEM6)}
	_, err = db.Model(secret).Where("name = ?", secret.Name).Update()
	require.NoError(t, err)

	rootCertPEM6, _, _, err := SetupServerCerts(db)
	require.Nil(t, rootCertPEM6)
	require.ErrorContains(t, err,
		"server certificates apparently were not generated by Stork",
	)

	// The root CA that wasn't generated by Stork server cannot be regenerated.
	rootKeyPEM7 := serverKeyPEM6
	template.IsCA = true
	certBytes, err = x509.CreateCertificate(rand.Reader, template, template,
		&privateKey.PublicKey, privateKey)
	require.NoError(t, err)
	block = pem.Block{Type: "CERTIFICATE", Bytes: certBytes}
	rootCertPEM7 := pem.EncodeToMemory(&block)

	secret = &dbmodel.Secret{Name: "cacert", Content: string(rootCertPEM7)}
	_, err = db.Model(secret).Where("name = ?", secret.Name).Update()
	require.NoError(t, err)

	secret = &dbmodel.Secret{Name: "cakey", Content: string(rootKeyPEM7)}
	_, err = db.Model(secret).Where("name = ?", secret.Name).Update()
	require.NoError(t, err)

	_, serverCertPEM7, _, err := SetupServerCerts(db)
	require.Nil(t, serverCertPEM7)
	require.ErrorContains(t, err,
		"These certificates no longer meet the "+
			"security standards required by Stork",
	)
	require.ErrorContains(t, err, "were not generated by Stork")
}

// Check if ExportSecret correctly exports various keys and
// certificates from a database to a file.
func TestExportSecret(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}

	db, _, teardown := dbtest.SetupDatabaseTestCase(t)
	defer teardown()

	sb := testutil.NewSandbox()
	defer sb.Close()

	// setup server certs for the first time - they should be generated
	rootCertPEM, serverCertPEM, serverKeyPEM, err := SetupServerCerts(db)
	require.NoError(t, err)

	// check exporting Server Cert
	err = ExportSecret(db, dbmodel.SecretServerCert, "")
	require.NoError(t, err)

	serverCertPEMFile, err := sb.Join("server-cert.pem")
	require.NoError(t, err)
	err = ExportSecret(db, dbmodel.SecretServerCert, serverCertPEMFile)
	require.NoError(t, err)
	serverCertPEM2, err := os.ReadFile(serverCertPEMFile)
	require.NoError(t, err)
	require.EqualValues(t, serverCertPEM, serverCertPEM2)

	// check exporting Server Key
	err = ExportSecret(db, dbmodel.SecretServerKey, "")
	require.NoError(t, err)

	serverKeyPEMFile, err := sb.Join("server-key.pem")
	require.NoError(t, err)
	err = ExportSecret(db, dbmodel.SecretServerKey, serverKeyPEMFile)
	require.NoError(t, err)
	serverKeyPEM2, err := os.ReadFile(serverKeyPEMFile)
	require.NoError(t, err)
	require.EqualValues(t, serverKeyPEM, serverKeyPEM2)

	// check exporting CA Cert
	err = ExportSecret(db, dbmodel.SecretCACert, "")
	require.NoError(t, err)

	rootCertPEMFile, err := sb.Join("root-cert.pem")
	require.NoError(t, err)
	err = ExportSecret(db, dbmodel.SecretCACert, rootCertPEMFile)
	require.NoError(t, err)
	rootCertPEM2, err := os.ReadFile(rootCertPEMFile)
	require.NoError(t, err)
	require.EqualValues(t, rootCertPEM, rootCertPEM2)

	// check exporting CA Key
	err = ExportSecret(db, dbmodel.SecretCAKey, "")
	require.NoError(t, err)

	rootKeyPEMFile, err := sb.Join("root-key.pem")
	require.NoError(t, err)
	err = ExportSecret(db, dbmodel.SecretCAKey, rootKeyPEMFile)
	require.NoError(t, err)
	rootKeyPEM2, err := os.ReadFile(rootKeyPEMFile)
	require.NoError(t, err)
	rootKeyPEM, err := dbmodel.GetSecret(db, dbmodel.SecretCAKey)
	require.NoError(t, err)
	require.EqualValues(t, rootKeyPEM, rootKeyPEM2)

	// check exporting Server Token
	err = ExportSecret(db, dbmodel.SecretServerToken, "")
	require.NoError(t, err)

	serverTokenFile, err := sb.Join("server-token.txt")
	require.NoError(t, err)
	err = ExportSecret(db, dbmodel.SecretServerToken, serverTokenFile)
	require.NoError(t, err)
	serverToken2, err := os.ReadFile(serverTokenFile)
	require.NoError(t, err)
	serverToken, err := dbmodel.GetSecret(db, dbmodel.SecretServerToken)
	require.NoError(t, err)
	require.EqualValues(t, serverToken, serverToken2)
}

// Check if ImportSecret correctly imports various keys and
// certificates from a file to a database.
func TestImportSecret(t *testing.T) {
	db, _, teardown := dbtest.SetupDatabaseTestCase(t)
	defer teardown()

	sb := testutil.NewSandbox()
	defer sb.Close()

	const CAKEY = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg2hWn2DtPcXa2QL6F
19NZkPHMgnX+7FyyGuji4jiHKkahRANCAAQmZt58pZ/G92lKd3z41lXGM5sNbhtz
/UA27ZOOk/Z2xoZcUj7gl3Qce91mgcGE6DaLB1jqL9omNN+2loNAfAIE
-----END PRIVATE KEY-----`

	const CACERT = `-----BEGIN CERTIFICATE-----
MIIBjDCCATKgAwIBAgIBATAKBggqhkjOPQQDAjAzMQswCQYDVQQGEwJVUzESMBAG
A1UEChMJSVNDIFN0b3JrMRAwDgYDVQQDEwdSb290IENBMCAXDTIxMDkwNjEyMjU0
N1oYDzIwNTEwOTA2MTIyNTQ3WjAzMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSVND
IFN0b3JrMRAwDgYDVQQDEwdSb290IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
QgAEJmbefKWfxvdpSnd8+NZVxjObDW4bc/1ANu2TjpP2dsaGXFI+4Jd0HHvdZoHB
hOg2iwdY6i/aJjTftpaDQHwCBKM1MDMwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNV
HQ4EFgQUU07u+8zyLNobqvJi4rtpsSrayu8wCgYIKoZIzj0EAwIDSAAwRQIhAPAf
YfThoFyxzukrwN16eMP8lX8tVwhyNMZ0aRu3S4vdAiBAcDx0tFt+rWIyFz7eCkeB
fVkdWL4LIJypZP53JBCFYg==
-----END CERTIFICATE-----`

	const SRVKEY = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgwxFLpLqRpR46bS46
27ukTFCwOcL6I6NNEpfWSE8R+1yhRANCAAQMJcAWsP3nDDZdXYkeZI+D+IFozFbW
HJ/kNaPkCQjuBN2t02BZu6bdr2p5rXcK2mMbxvvjJhSXrBS0/jpsJKZs
-----END PRIVATE KEY-----`

	const SRVCERT = `-----BEGIN CERTIFICATE-----
MIICxDCCAmmgAwIBAgIBAjAKBggqhkjOPQQDAjAzMQswCQYDVQQGEwJVUzESMBAG
A1UEChMJSVNDIFN0b3JrMRAwDgYDVQQDEwdSb290IENBMCAXDTIxMDkwNjEyMjU1
MFoYDzIwNTEwOTA2MTIyNTUwWjBGMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSVND
IFN0b3JrMQ8wDQYDVQQLEwZzZXJ2ZXIxEjAQBgNVBAMTCWxvY2FsaG9zdDBZMBMG
ByqGSM49AgEGCCqGSM49AwEHA0IABAwlwBaw/ecMNl1diR5kj4P4gWjMVtYcn+Q1
o+QJCO4E3a3TYFm7pt2vanmtdwraYxvG++MmFJesFLT+OmwkpmyjggFXMIIBUzAf
BgNVHSMEGDAWgBRTTu77zPIs2huq8mLiu2mxKtrK7zCCAS4GA1UdEQSCASUwggEh
gglsb2NhbGhvc3SCBXR5Y2hvggV0eWNob4IFdHljaG+CDWlwNi1sb2NhbGhvc3SC
BXR5Y2hvggV0eWNob4IFdHljaG+CBXR5Y2hvggV0eWNob4IFdHljaG+CBXR5Y2hv
ggV0eWNob4cEfwAAAYcEwKgBY4cEwKh6AYcErBEAAYcQAAAAAAAAAAAAAAAAAAAA
AYcQIAEEcGOJAAAAAAAAAAAOLYcQ/Qq4KPD/AAAAAAAAAAAOLYcQ/Qq4KPD/AAD0
y69/jhT1zYcQ/Qq4KPD/AAAWRgqhR1EjJIcQIAEEcGOJAAAMjU3ezH+W/ocQIAEE
cGOJAABrU7hrjAdOgIcQ/oAAAAAAAADxjY42pn4t9IcQ/oAAAAAAAAAAQiX//obP
5DAKBggqhkjOPQQDAgNJADBGAiEAywycleZPDX5adSLRCghFA8476nVYmGlkwA7+
hbkkHg8CIQDEfP1HGySpXF5AhAK5RSIxSJTvVhzSSMKtAEmqG2BgYw==
-----END CERTIFICATE-----`

	const SRVTKN = "f3T93fi31WCNSFd7v62hm9f9lsJ7MoHt"

	// check importing Server Cert
	serverCertPEMFile, err := sb.Write("server-cert.pem", SRVCERT)
	require.NoError(t, err)
	err = ImportSecret(db, dbmodel.SecretServerCert, serverCertPEMFile)
	require.NoError(t, err)
	serverCertPEM, err := dbmodel.GetSecret(db, dbmodel.SecretServerCert)
	require.NoError(t, err)
	require.EqualValues(t, serverCertPEM, SRVCERT)

	// check importing Server Key
	serverKeyPEMFile, err := sb.Write("server-key.pem", SRVKEY)
	require.NoError(t, err)
	err = ImportSecret(db, dbmodel.SecretServerKey, serverKeyPEMFile)
	require.NoError(t, err)
	serverKeyPEM, err := dbmodel.GetSecret(db, dbmodel.SecretServerKey)
	require.NoError(t, err)
	require.EqualValues(t, SRVKEY, serverKeyPEM)

	// check importing CA Cert
	rootCertPEMFile, err := sb.Write("root-cert.pem", CACERT)
	require.NoError(t, err)
	err = ImportSecret(db, dbmodel.SecretCACert, rootCertPEMFile)
	require.NoError(t, err)
	rootCertPEM, err := dbmodel.GetSecret(db, dbmodel.SecretCACert)
	require.NoError(t, err)
	require.EqualValues(t, CACERT, rootCertPEM)

	// check importing CA Key
	rootKeyPEMFile, err := sb.Write("root-key.pem", CAKEY)
	require.NoError(t, err)
	err = ImportSecret(db, dbmodel.SecretCAKey, rootKeyPEMFile)
	require.NoError(t, err)
	rootKeyPEM, err := dbmodel.GetSecret(db, dbmodel.SecretCAKey)
	require.NoError(t, err)
	require.EqualValues(t, CAKEY, rootKeyPEM)

	// check importing Server Token
	serverTokenFile, err := sb.Write("server-token.txt", SRVTKN)
	require.NoError(t, err)
	err = ImportSecret(db, dbmodel.SecretServerToken, serverTokenFile)
	require.NoError(t, err)
	serverToken, err := dbmodel.GetSecret(db, dbmodel.SecretServerToken)
	require.NoError(t, err)
	require.EqualValues(t, SRVTKN, serverToken)

	// Check that importing non-existing file fails.
	err = ImportSecret(db, dbmodel.SecretServerCert, "nonexistent.txt")
	require.Error(t, err)
	err = ImportSecret(db, dbmodel.SecretServerKey, "nonexistent.txt")
	require.Error(t, err)
	err = ImportSecret(db, dbmodel.SecretCACert, "nonexistent.txt")
	require.Error(t, err)
	err = ImportSecret(db, dbmodel.SecretCAKey, "nonexistent.txt")
	require.Error(t, err)
	err = ImportSecret(db, dbmodel.SecretServerToken, "nonexistent.txt")
	require.Error(t, err)

	// Check that importing nonsense instead of valid PEM files is rejected.
	nonsenseFile, _ := sb.Write("nonsense.pem", "the Earth is flat")
	err = ImportSecret(db, dbmodel.SecretServerCert, nonsenseFile)
	require.Error(t, err)
	// Key validation is not implemented yet.
	err = ImportSecret(db, dbmodel.SecretServerKey, nonsenseFile)
	require.Error(t, err)
	err = ImportSecret(db, dbmodel.SecretCACert, nonsenseFile)
	require.Error(t, err)
	// Key validation is not implemented yet.
	err = ImportSecret(db, dbmodel.SecretCAKey, nonsenseFile)
	require.Error(t, err)
	err = ImportSecret(db, dbmodel.SecretServerToken, nonsenseFile)
	require.Error(t, err)
}

// Test that the root CA certificate must be regenerated if it doesn't meet
// the requirements.
func TestVerifyRootCACustomRequirements(t *testing.T) {
	// Arrange
	validKeyFactory := func() *x509.Certificate {
		return &x509.Certificate{
			KeyUsage:    x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
			ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		}
	}

	validKey := validKeyFactory()

	// Act
	err := verifyRootCACustomRequirements(validKey)

	// Assert
	require.NoError(t, err)

	t.Run("empty key usage", func(t *testing.T) {
		// Arrange
		cert := validKeyFactory()
		cert.KeyUsage = 0

		// Act
		err := verifyRootCACustomRequirements(cert)

		// Assert
		require.ErrorContains(t, err, "invalid key usage")
	})

	t.Run("missing required key usage", func(t *testing.T) {
		// Arrange
		cert := validKeyFactory()
		cert.KeyUsage = x509.KeyUsageDigitalSignature

		// Act
		err := verifyRootCACustomRequirements(cert)

		// Assert
		require.ErrorContains(t, err, "invalid key usage")
	})

	t.Run("extra key usage", func(t *testing.T) {
		// Arrange
		cert := validKeyFactory()
		cert.KeyUsage |= x509.KeyUsageKeyEncipherment

		// Act
		err := verifyRootCACustomRequirements(cert)

		// Assert
		require.ErrorContains(t, err, "invalid key usage")
	})

	t.Run("empty extended key usage", func(t *testing.T) {
		// Arrange
		cert := validKeyFactory()
		cert.ExtKeyUsage = nil

		// Act
		err := verifyRootCACustomRequirements(cert)

		// Assert
		require.ErrorContains(t, err,
			"missing mandatory extended key usage: Client Authentication",
		)
		require.ErrorContains(t, err,
			"missing mandatory extended key usage: Server Authentication",
		)
	})

	t.Run("missing client auth extended key usage", func(t *testing.T) {
		// Arrange
		cert := validKeyFactory()
		cert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}

		// Act
		err := verifyRootCACustomRequirements(cert)

		// Assert
		require.ErrorContains(t, err,
			"missing mandatory extended key usage: Client Authentication",
		)
	})

	t.Run("missing server auth extended key usage", func(t *testing.T) {
		// Arrange
		cert := validKeyFactory()
		cert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}

		// Act
		err := verifyRootCACustomRequirements(cert)

		// Assert
		require.ErrorContains(t, err,
			"missing mandatory extended key usage: Server Authentication",
		)
	})

	t.Run("extra extended key usage", func(t *testing.T) {
		// Arrange
		cert := validKeyFactory()
		cert.ExtKeyUsage = append(cert.ExtKeyUsage, x509.ExtKeyUsageCodeSigning)

		// Act
		err := verifyRootCACustomRequirements(cert)

		// Assert
		require.NoError(t, err)
	})

	t.Run("multiple issues should be reported together", func(t *testing.T) {
		// Arrange
		cert := validKeyFactory()
		cert.KeyUsage = 0
		cert.ExtKeyUsage = nil

		// Act
		err := verifyRootCACustomRequirements(cert)

		// Assert
		require.ErrorContains(t, err, "invalid key usage")
		require.ErrorContains(t, err,
			"missing mandatory extended key usage: Client Authentication",
		)
		require.ErrorContains(t, err,
			"missing mandatory extended key usage: Server Authentication",
		)
	})
}
