1package atccmd
2
3import (
4	"context"
5	"database/sql"
6
7	"github.com/concourse/concourse/atc/db"
8	"github.com/concourse/concourse/atc/db/encryption"
9	"golang.org/x/crypto/acme/autocert"
10)
11
12type dbCache struct {
13	get, put, delete *sql.Stmt
14	es               encryption.Strategy
15}
16
17func newDbCache(conn db.Conn) (autocert.Cache, error) {
18	c := new(dbCache)
19	c.es = conn.EncryptionStrategy()
20	var err error
21	c.get, err = conn.Prepare("SELECT cert, nonce FROM cert_cache WHERE domain = $1")
22	if err != nil {
23		return nil, err
24	}
25	c.put, err = conn.Prepare("INSERT INTO cert_cache (domain, cert, nonce) VALUES ($1, $2, $3) ON CONFLICT (domain) DO UPDATE SET domain = EXCLUDED.domain, cert = EXCLUDED.cert, nonce = EXCLUDED.nonce")
26	if err != nil {
27		return nil, err
28	}
29	c.delete, err = conn.Prepare("DELETE FROM cert_cache WHERE domain = $1")
30	return c, err
31}
32
33func (c *dbCache) Get(ctx context.Context, domain string) ([]byte, error) {
34	var ciphertext string
35	var nonce sql.NullString
36	err := c.get.QueryRowContext(ctx, domain).Scan(&ciphertext, &nonce)
37	if err == sql.ErrNoRows {
38		err = autocert.ErrCacheMiss
39	}
40	if err != nil {
41		return nil, err
42	}
43	var noncense *string
44	if nonce.Valid {
45		noncense = &nonce.String
46	}
47	return c.es.Decrypt(ciphertext, noncense)
48}
49
50func (c *dbCache) Put(ctx context.Context, domain string, cert []byte) error {
51	ciphertext, nonce, err := c.es.Encrypt(cert)
52	if err != nil {
53		return err
54	}
55	_, err = c.put.ExecContext(ctx, domain, ciphertext, nonce)
56	return err
57}
58
59func (c *dbCache) Delete(ctx context.Context, domain string) error {
60	_, err := c.delete.ExecContext(ctx, domain)
61	return err
62}
63