sa: use internal certificateStatusModel instead of core.CertificateStatus (#8159)

Part of https://github.com/letsencrypt/boulder/issues/8112
This commit is contained in:
Jacob Hoffman-Andrews 2025-05-12 14:53:08 -07:00 committed by GitHub
parent 01a299cd0f
commit 388c68cb49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 76 additions and 74 deletions

View File

@ -516,15 +516,18 @@ func insertCertificate(cert certDERWithRegID, lastNagSent time.Time) error {
return fmt.Errorf("inserting certificate: %w", err) return fmt.Errorf("inserting certificate: %w", err)
} }
return setupDBMap.Insert(ctx, &core.CertificateStatus{ _, err = setupDBMap.ExecContext(context.Background(),
Serial: core.SerialToString(parsedCert.SerialNumber), `INSERT INTO certificateStatus
LastExpirationNagSent: lastNagSent, (serial, notAfter, status, ocspLastUpdated, revokedDate, revokedReason, lastExpirationNagSent)
Status: core.OCSPStatusGood, VALUES (?, ?, ?, ?, ?, ?, ?)`,
NotAfter: parsedCert.NotAfter, core.SerialToString(parsedCert.SerialNumber),
OCSPLastUpdated: time.Time{}, parsedCert.NotAfter,
RevokedDate: time.Time{}, core.OCSPStatusGood,
RevokedReason: 0, time.Time{},
}) time.Time{},
0,
lastNagSent)
return err
} }
func addExpiringCerts(t *testing.T, ctx *testCtx) []certDERWithRegID { func addExpiringCerts(t *testing.T, ctx *testCtx) []certDERWithRegID {

View File

@ -15,6 +15,7 @@ import (
capb "github.com/letsencrypt/boulder/ca/proto" capb "github.com/letsencrypt/boulder/ca/proto"
"github.com/letsencrypt/boulder/cmd" "github.com/letsencrypt/boulder/cmd"
"github.com/letsencrypt/boulder/core" "github.com/letsencrypt/boulder/core"
"github.com/letsencrypt/boulder/db"
blog "github.com/letsencrypt/boulder/log" blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/metrics" "github.com/letsencrypt/boulder/metrics"
"github.com/letsencrypt/boulder/rocsp" "github.com/letsencrypt/boulder/rocsp"
@ -50,29 +51,34 @@ func makeClient() (*rocsp.RWClient, clock.Clock) {
return rocsp.NewWritingClient(rdb, 500*time.Millisecond, clk, metrics.NoopRegisterer), clk return rocsp.NewWritingClient(rdb, 500*time.Millisecond, clk, metrics.NoopRegisterer), clk
} }
func TestGetStartingID(t *testing.T) { func insertCertificateStatus(t *testing.T, dbMap db.Executor, serial string, notAfter, ocspLastUpdated time.Time) int64 {
ctx := context.Background() result, err := dbMap.ExecContext(context.Background(),
`INSERT INTO certificateStatus
(serial, notAfter, status, ocspLastUpdated, revokedDate, revokedReason, lastExpirationNagSent, issuerID)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
serial,
notAfter,
core.OCSPStatusGood,
ocspLastUpdated,
time.Time{},
0,
time.Time{},
99)
test.AssertNotError(t, err, "inserting certificate status")
id, err := result.LastInsertId()
test.AssertNotError(t, err, "getting last insert ID")
return id
}
func TestGetStartingID(t *testing.T) {
clk := clock.NewFake() clk := clock.NewFake()
dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms) dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms)
test.AssertNotError(t, err, "failed setting up db client") test.AssertNotError(t, err, "failed setting up db client")
defer test.ResetBoulderTestDatabase(t)() defer test.ResetBoulderTestDatabase(t)()
cs := core.CertificateStatus{ firstID := insertCertificateStatus(t, dbMap, "1337", clk.Now().Add(12*time.Hour), time.Time{})
Serial: "1337", secondID := insertCertificateStatus(t, dbMap, "1338", clk.Now().Add(36*time.Hour), time.Time{})
NotAfter: clk.Now().Add(12 * time.Hour),
}
err = dbMap.Insert(ctx, &cs)
test.AssertNotError(t, err, "inserting certificate status")
firstID := cs.ID
cs = core.CertificateStatus{
Serial: "1338",
NotAfter: clk.Now().Add(36 * time.Hour),
}
err = dbMap.Insert(ctx, &cs)
test.AssertNotError(t, err, "inserting certificate status")
secondID := cs.ID
t.Logf("first ID %d, second ID %d", firstID, secondID) t.Logf("first ID %d, second ID %d", firstID, secondID)
clk.Sleep(48 * time.Hour) clk.Sleep(48 * time.Hour)
@ -131,11 +137,7 @@ func TestLoadFromDB(t *testing.T) {
defer test.ResetBoulderTestDatabase(t) defer test.ResetBoulderTestDatabase(t)
for i := range 100 { for i := range 100 {
err = dbMap.Insert(context.Background(), &core.CertificateStatus{ insertCertificateStatus(t, dbMap, fmt.Sprintf("%036x", i), clk.Now().Add(200*time.Hour), clk.Now())
Serial: fmt.Sprintf("%036x", i),
NotAfter: clk.Now().Add(200 * time.Hour),
OCSPLastUpdated: clk.Now(),
})
if err != nil { if err != nil {
t.Fatalf("Failed to insert certificateStatus: %s", err) t.Fatalf("Failed to insert certificateStatus: %s", err)
} }

View File

@ -18,7 +18,6 @@ import (
corepb "github.com/letsencrypt/boulder/core/proto" corepb "github.com/letsencrypt/boulder/core/proto"
"github.com/letsencrypt/boulder/identifier" "github.com/letsencrypt/boulder/identifier"
"github.com/letsencrypt/boulder/probs" "github.com/letsencrypt/boulder/probs"
"github.com/letsencrypt/boulder/revocation"
sapb "github.com/letsencrypt/boulder/sa/proto" sapb "github.com/letsencrypt/boulder/sa/proto"
vapb "github.com/letsencrypt/boulder/va/proto" vapb "github.com/letsencrypt/boulder/va/proto"
) )
@ -343,34 +342,6 @@ func newOrderValid(order *corepb.Order) bool {
return !(order.RegistrationID == 0 || order.Expires == nil || len(order.Identifiers) == 0) return !(order.RegistrationID == 0 || order.Expires == nil || len(order.Identifiers) == 0)
} }
func CertStatusToPB(certStatus core.CertificateStatus) *corepb.CertificateStatus {
return &corepb.CertificateStatus{
Serial: certStatus.Serial,
Status: string(certStatus.Status),
OcspLastUpdated: timestamppb.New(certStatus.OCSPLastUpdated),
RevokedDate: timestamppb.New(certStatus.RevokedDate),
RevokedReason: int64(certStatus.RevokedReason),
LastExpirationNagSent: timestamppb.New(certStatus.LastExpirationNagSent),
NotAfter: timestamppb.New(certStatus.NotAfter),
IsExpired: certStatus.IsExpired,
IssuerID: certStatus.IssuerNameID,
}
}
func PBToCertStatus(pb *corepb.CertificateStatus) core.CertificateStatus {
return core.CertificateStatus{
Serial: pb.Serial,
Status: core.OCSPStatus(pb.Status),
OCSPLastUpdated: pb.OcspLastUpdated.AsTime(),
RevokedDate: pb.RevokedDate.AsTime(),
RevokedReason: revocation.Reason(pb.RevokedReason),
LastExpirationNagSent: pb.LastExpirationNagSent.AsTime(),
NotAfter: pb.NotAfter.AsTime(),
IsExpired: pb.IsExpired,
IssuerNameID: pb.IssuerID,
}
}
// PBToAuthzMap converts a protobuf map of domains mapped to protobuf authorizations to a // PBToAuthzMap converts a protobuf map of domains mapped to protobuf authorizations to a
// golang map[string]*core.Authorization. // golang map[string]*core.Authorization.
func PBToAuthzMap(pb *sapb.Authorizations) (map[identifier.ACMEIdentifier]*core.Authorization, error) { func PBToAuthzMap(pb *sapb.Authorizations) (map[identifier.ACMEIdentifier]*core.Authorization, error) {

View File

@ -270,7 +270,7 @@ func initTables(dbMap *borp.DbMap) {
regTable.ColMap("KeySHA256").SetNotNull(true).SetUnique(true) regTable.ColMap("KeySHA256").SetNotNull(true).SetUnique(true)
dbMap.AddTableWithName(issuedNameModel{}, "issuedNames").SetKeys(true, "ID") dbMap.AddTableWithName(issuedNameModel{}, "issuedNames").SetKeys(true, "ID")
dbMap.AddTableWithName(core.Certificate{}, "certificates").SetKeys(true, "ID") dbMap.AddTableWithName(core.Certificate{}, "certificates").SetKeys(true, "ID")
dbMap.AddTableWithName(core.CertificateStatus{}, "certificateStatus").SetKeys(true, "ID") dbMap.AddTableWithName(certificateStatusModel{}, "certificateStatus").SetKeys(true, "ID")
dbMap.AddTableWithName(core.FQDNSet{}, "fqdnSets").SetKeys(true, "ID") dbMap.AddTableWithName(core.FQDNSet{}, "fqdnSets").SetKeys(true, "ID")
tableMap := dbMap.AddTableWithName(orderModel{}, "orders").SetKeys(true, "ID") tableMap := dbMap.AddTableWithName(orderModel{}, "orders").SetKeys(true, "ID")
if !features.Get().StoreARIReplacesInOrders { if !features.Get().StoreARIReplacesInOrders {

View File

@ -212,15 +212,15 @@ const certStatusFields = "id, serial, status, ocspLastUpdated, revokedDate, revo
// SelectCertificateStatus selects all fields of one certificate status model // SelectCertificateStatus selects all fields of one certificate status model
// identified by serial // identified by serial
func SelectCertificateStatus(ctx context.Context, s db.OneSelector, serial string) (core.CertificateStatus, error) { func SelectCertificateStatus(ctx context.Context, s db.OneSelector, serial string) (*corepb.CertificateStatus, error) {
var model core.CertificateStatus var model certificateStatusModel
err := s.SelectOne( err := s.SelectOne(
ctx, ctx,
&model, &model,
"SELECT "+certStatusFields+" FROM certificateStatus WHERE serial = ? LIMIT 1", "SELECT "+certStatusFields+" FROM certificateStatus WHERE serial = ? LIMIT 1",
serial, serial,
) )
return model, err return model.toPb(), err
} }
// RevocationStatusModel represents a small subset of the columns in the // RevocationStatusModel represents a small subset of the columns in the
@ -393,6 +393,33 @@ func (model certificateModel) toPb() *corepb.Certificate {
} }
} }
type certificateStatusModel struct {
ID int64 `db:"id"`
Serial string `db:"serial"`
Status core.OCSPStatus `db:"status"`
OCSPLastUpdated time.Time `db:"ocspLastUpdated"`
RevokedDate time.Time `db:"revokedDate"`
RevokedReason revocation.Reason `db:"revokedReason"`
LastExpirationNagSent time.Time `db:"lastExpirationNagSent"`
NotAfter time.Time `db:"notAfter"`
IsExpired bool `db:"isExpired"`
IssuerID int64 `db:"issuerID"`
}
func (model certificateStatusModel) toPb() *corepb.CertificateStatus {
return &corepb.CertificateStatus{
Serial: model.Serial,
Status: string(model.Status),
OcspLastUpdated: timestamppb.New(model.OCSPLastUpdated),
RevokedDate: timestamppb.New(model.RevokedDate),
RevokedReason: int64(model.RevokedReason),
LastExpirationNagSent: timestamppb.New(model.LastExpirationNagSent),
NotAfter: timestamppb.New(model.NotAfter),
IsExpired: model.IsExpired,
IssuerID: model.IssuerID,
}
}
// orderModel represents one row in the orders table. The CertificateProfileName // orderModel represents one row in the orders table. The CertificateProfileName
// column is a pointer because the column is NULL-able. // column is a pointer because the column is NULL-able.
type orderModel struct { type orderModel struct {

View File

@ -330,7 +330,7 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb
if req.OcspNotReady { if req.OcspNotReady {
status = core.OCSPStatusNotReady status = core.OCSPStatusNotReady
} }
cs := &core.CertificateStatus{ cs := &certificateStatusModel{
Serial: serialHex, Serial: serialHex,
Status: status, Status: status,
OCSPLastUpdated: ssa.clk.Now(), OCSPLastUpdated: ssa.clk.Now(),
@ -339,7 +339,7 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb
LastExpirationNagSent: time.Time{}, LastExpirationNagSent: time.Time{},
NotAfter: parsed.NotAfter, NotAfter: parsed.NotAfter,
IsExpired: false, IsExpired: false,
IssuerNameID: req.IssuerNameID, IssuerID: req.IssuerNameID,
} }
err = ssa.dbMap.Insert(ctx, cs) err = ssa.dbMap.Insert(ctx, cs)
if err != nil { if err != nil {
@ -1374,9 +1374,9 @@ func (ssa *SQLStorageAuthority) PauseIdentifiers(ctx context.Context, req *sapb.
err := tx.SelectOne(ctx, &entry, ` err := tx.SelectOne(ctx, &entry, `
SELECT pausedAt, unpausedAt SELECT pausedAt, unpausedAt
FROM paused FROM paused
WHERE WHERE
registrationID = ? AND registrationID = ? AND
identifierType = ? AND identifierType = ? AND
identifierValue = ?`, identifierValue = ?`,
req.RegistrationID, req.RegistrationID,
ident.Type, ident.Type,
@ -1420,9 +1420,9 @@ func (ssa *SQLStorageAuthority) PauseIdentifiers(ctx context.Context, req *sapb.
UPDATE paused UPDATE paused
SET pausedAt = ?, SET pausedAt = ?,
unpausedAt = NULL unpausedAt = NULL
WHERE WHERE
registrationID = ? AND registrationID = ? AND
identifierType = ? AND identifierType = ? AND
identifierValue = ? AND identifierValue = ? AND
unpausedAt IS NOT NULL`, unpausedAt IS NOT NULL`,
ssa.clk.Now().Truncate(time.Second), ssa.clk.Now().Truncate(time.Second),
@ -1468,7 +1468,7 @@ func (ssa *SQLStorageAuthority) UnpauseAccount(ctx context.Context, req *sapb.Re
result, err := ssa.dbMap.ExecContext(ctx, ` result, err := ssa.dbMap.ExecContext(ctx, `
UPDATE paused UPDATE paused
SET unpausedAt = ? SET unpausedAt = ?
WHERE WHERE
registrationID = ? AND registrationID = ? AND
unpausedAt IS NULL unpausedAt IS NULL
LIMIT ?`, LIMIT ?`,

View File

@ -20,7 +20,6 @@ import (
corepb "github.com/letsencrypt/boulder/core/proto" corepb "github.com/letsencrypt/boulder/core/proto"
"github.com/letsencrypt/boulder/db" "github.com/letsencrypt/boulder/db"
berrors "github.com/letsencrypt/boulder/errors" berrors "github.com/letsencrypt/boulder/errors"
bgrpc "github.com/letsencrypt/boulder/grpc"
"github.com/letsencrypt/boulder/identifier" "github.com/letsencrypt/boulder/identifier"
blog "github.com/letsencrypt/boulder/log" blog "github.com/letsencrypt/boulder/log"
sapb "github.com/letsencrypt/boulder/sa/proto" sapb "github.com/letsencrypt/boulder/sa/proto"
@ -258,7 +257,7 @@ func (ssa *SQLStorageAuthorityRO) GetCertificateStatus(ctx context.Context, req
return nil, err return nil, err
} }
return bgrpc.CertStatusToPB(certStatus), nil return certStatus, nil
} }
// GetRevocationStatus takes a hexadecimal string representing the full serial // GetRevocationStatus takes a hexadecimal string representing the full serial