github.com/letsencrypt/boulder@v0.20251208.0/sa/sa_test.go (about) 1 package sa 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/ecdsa" 7 "crypto/elliptic" 8 "crypto/rand" 9 "crypto/sha256" 10 "crypto/x509" 11 "database/sql" 12 "encoding/base64" 13 "encoding/json" 14 "errors" 15 "fmt" 16 "io" 17 "math/big" 18 "math/bits" 19 mrand "math/rand/v2" 20 "net/netip" 21 "os" 22 "reflect" 23 "slices" 24 "strconv" 25 "strings" 26 "testing" 27 "time" 28 29 "github.com/go-jose/go-jose/v4" 30 "github.com/go-sql-driver/mysql" 31 "github.com/jmhodges/clock" 32 "github.com/prometheus/client_golang/prometheus" 33 "google.golang.org/grpc" 34 "google.golang.org/protobuf/types/known/durationpb" 35 "google.golang.org/protobuf/types/known/emptypb" 36 "google.golang.org/protobuf/types/known/timestamppb" 37 38 "github.com/letsencrypt/boulder/core" 39 corepb "github.com/letsencrypt/boulder/core/proto" 40 "github.com/letsencrypt/boulder/db" 41 berrors "github.com/letsencrypt/boulder/errors" 42 "github.com/letsencrypt/boulder/features" 43 bgrpc "github.com/letsencrypt/boulder/grpc" 44 "github.com/letsencrypt/boulder/identifier" 45 blog "github.com/letsencrypt/boulder/log" 46 "github.com/letsencrypt/boulder/metrics" 47 "github.com/letsencrypt/boulder/probs" 48 "github.com/letsencrypt/boulder/revocation" 49 sapb "github.com/letsencrypt/boulder/sa/proto" 50 "github.com/letsencrypt/boulder/test" 51 "github.com/letsencrypt/boulder/test/vars" 52 ) 53 54 var log = blog.UseMock() 55 var ctx = context.Background() 56 57 var ( 58 theKey = `{ 59 "kty": "RSA", 60 "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", 61 "e": "AQAB" 62 }` 63 ) 64 65 func mustTime(s string) time.Time { 66 t, err := time.Parse("2006-01-02 15:04", s) 67 if err != nil { 68 panic(fmt.Sprintf("parsing %q: %s", s, err)) 69 } 70 return t.UTC() 71 } 72 73 func mustTimestamp(s string) *timestamppb.Timestamp { 74 return timestamppb.New(mustTime(s)) 75 } 76 77 type fakeServerStream[T any] struct { 78 grpc.ServerStream 79 output chan<- *T 80 } 81 82 func (s *fakeServerStream[T]) Send(msg *T) error { 83 s.output <- msg 84 return nil 85 } 86 87 func (s *fakeServerStream[T]) Context() context.Context { 88 return context.Background() 89 } 90 91 // initSA constructs a SQLStorageAuthority and a clean up function that should 92 // be defer'ed to the end of the test. 93 func initSA(t testing.TB) (*SQLStorageAuthority, clock.FakeClock, func()) { 94 t.Helper() 95 features.Reset() 96 97 dbMap, err := DBMapForTest(vars.DBConnSA) 98 if err != nil { 99 t.Fatalf("Failed to create dbMap: %s", err) 100 } 101 102 dbIncidentsMap, err := DBMapForTest(vars.DBConnIncidents) 103 if err != nil { 104 t.Fatalf("Failed to create dbMap: %s", err) 105 } 106 107 fc := clock.NewFake() 108 fc.Set(mustTime("2015-03-04 05:00")) 109 110 saro, err := NewSQLStorageAuthorityRO(dbMap, dbIncidentsMap, metrics.NoopRegisterer, 1, 0, fc, log) 111 if err != nil { 112 t.Fatalf("Failed to create SA: %s", err) 113 } 114 115 sa, err := NewSQLStorageAuthorityWrapping(saro, dbMap, metrics.NoopRegisterer) 116 if err != nil { 117 t.Fatalf("Failed to create SA: %s", err) 118 } 119 120 return sa, fc, test.ResetBoulderTestDatabase(t) 121 } 122 123 // CreateWorkingTestRegistration inserts a new, correct Registration into the 124 // given SA. 125 func createWorkingRegistration(t testing.TB, sa *SQLStorageAuthority) *corepb.Registration { 126 reg, err := sa.NewRegistration(context.Background(), &corepb.Registration{ 127 Key: []byte(theKey), 128 CreatedAt: mustTimestamp("2003-05-10 00:00"), 129 Status: string(core.StatusValid), 130 }) 131 if err != nil { 132 t.Fatalf("Unable to create new registration: %s", err) 133 } 134 return reg 135 } 136 137 func createPendingAuthorization(t *testing.T, sa *SQLStorageAuthority, regID int64, ident identifier.ACMEIdentifier, exp time.Time) int64 { 138 t.Helper() 139 140 tokenStr := core.NewToken() 141 token, err := base64.RawURLEncoding.DecodeString(tokenStr) 142 test.AssertNotError(t, err, "computing test authorization challenge token") 143 144 am := authzModel{ 145 IdentifierType: identifierTypeToUint[string(ident.Type)], 146 IdentifierValue: ident.Value, 147 RegistrationID: regID, 148 Status: statusToUint[core.StatusPending], 149 Expires: exp, 150 Challenges: 1 << challTypeToUint[string(core.ChallengeTypeHTTP01)], 151 Token: token, 152 } 153 154 err = sa.dbMap.Insert(context.Background(), &am) 155 test.AssertNotError(t, err, "creating test authorization") 156 157 return am.ID 158 } 159 160 func createFinalizedAuthorization(t *testing.T, sa *SQLStorageAuthority, regID int64, ident identifier.ACMEIdentifier, exp time.Time, 161 status string, attemptedAt time.Time) int64 { 162 t.Helper() 163 pendingID := createPendingAuthorization(t, sa, regID, ident, exp) 164 attempted := string(core.ChallengeTypeHTTP01) 165 _, err := sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{ 166 Id: pendingID, 167 Status: status, 168 Expires: timestamppb.New(exp), 169 Attempted: attempted, 170 AttemptedAt: timestamppb.New(attemptedAt), 171 }) 172 test.AssertNotError(t, err, "sa.FinalizeAuthorizations2 failed") 173 return pendingID 174 } 175 176 func goodTestJWK() *jose.JSONWebKey { 177 var jwk jose.JSONWebKey 178 err := json.Unmarshal([]byte(theKey), &jwk) 179 if err != nil { 180 panic("known-good theKey is no longer known-good") 181 } 182 return &jwk 183 } 184 185 func TestAddRegistration(t *testing.T) { 186 sa, clk, cleanUp := initSA(t) 187 defer cleanUp() 188 189 jwkJSON, _ := goodTestJWK().MarshalJSON() 190 reg, err := sa.NewRegistration(ctx, &corepb.Registration{ 191 Key: jwkJSON, 192 }) 193 if err != nil { 194 t.Fatalf("Couldn't create new registration: %s", err) 195 } 196 test.Assert(t, reg.Id != 0, "ID shouldn't be 0") 197 198 // Confirm that the registration can be retrieved by ID. 199 dbReg, err := sa.GetRegistration(ctx, &sapb.RegistrationID{Id: reg.Id}) 200 test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.Id)) 201 202 createdAt := clk.Now() 203 test.AssertEquals(t, dbReg.Id, reg.Id) 204 test.AssertByteEquals(t, dbReg.Key, jwkJSON) 205 test.AssertDeepEquals(t, dbReg.CreatedAt.AsTime(), createdAt) 206 207 _, err = sa.GetRegistration(ctx, &sapb.RegistrationID{Id: 0}) 208 test.AssertError(t, err, "Registration object for ID 0 was returned") 209 210 // Confirm that the registration can be retrieved by key. 211 dbReg, err = sa.GetRegistrationByKey(ctx, &sapb.JSONWebKey{Jwk: jwkJSON}) 212 test.AssertNotError(t, err, "Couldn't get registration by key") 213 test.AssertEquals(t, dbReg.Id, dbReg.Id) 214 test.AssertEquals(t, dbReg.Agreement, dbReg.Agreement) 215 216 anotherKey := `{ 217 "kty":"RSA", 218 "n": "vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw", 219 "e":"AQAB" 220 }` 221 _, err = sa.GetRegistrationByKey(ctx, &sapb.JSONWebKey{Jwk: []byte(anotherKey)}) 222 test.AssertError(t, err, "Registration object for invalid key was returned") 223 } 224 225 func TestNoSuchRegistrationErrors(t *testing.T) { 226 sa, _, cleanUp := initSA(t) 227 defer cleanUp() 228 229 _, err := sa.GetRegistration(ctx, &sapb.RegistrationID{Id: 100}) 230 test.AssertErrorIs(t, err, berrors.NotFound) 231 232 jwk := goodTestJWK() 233 jwkJSON, _ := jwk.MarshalJSON() 234 235 _, err = sa.GetRegistrationByKey(ctx, &sapb.JSONWebKey{Jwk: jwkJSON}) 236 test.AssertErrorIs(t, err, berrors.NotFound) 237 238 _, err = sa.UpdateRegistrationKey(ctx, &sapb.UpdateRegistrationKeyRequest{RegistrationID: 100, Jwk: jwkJSON}) 239 test.AssertErrorIs(t, err, berrors.InternalServer) 240 } 241 242 func TestSelectRegistration(t *testing.T) { 243 sa, _, cleanUp := initSA(t) 244 defer cleanUp() 245 var ctx = context.Background() 246 jwk := goodTestJWK() 247 jwkJSON, _ := jwk.MarshalJSON() 248 sha, err := core.KeyDigestB64(jwk.Key) 249 test.AssertNotError(t, err, "couldn't parse jwk.Key") 250 251 reg, err := sa.NewRegistration(ctx, &corepb.Registration{ 252 Key: jwkJSON, 253 }) 254 test.AssertNotError(t, err, fmt.Sprintf("couldn't create new registration: %s", err)) 255 test.Assert(t, reg.Id != 0, "ID shouldn't be 0") 256 257 _, err = selectRegistration(ctx, sa.dbMap, "id", reg.Id) 258 test.AssertNotError(t, err, "selecting by id should work") 259 _, err = selectRegistration(ctx, sa.dbMap, "jwk_sha256", sha) 260 test.AssertNotError(t, err, "selecting by jwk_sha256 should work") 261 } 262 263 func TestReplicationLagRetries(t *testing.T) { 264 sa, clk, cleanUp := initSA(t) 265 defer cleanUp() 266 267 reg := createWorkingRegistration(t, sa) 268 269 // First, set the lagFactor to 0. Neither selecting a real registration nor 270 // selecting a nonexistent registration should cause the clock to advance. 271 sa.lagFactor = 0 272 start := clk.Now() 273 274 _, err := sa.GetRegistration(ctx, &sapb.RegistrationID{Id: reg.Id}) 275 test.AssertNotError(t, err, "selecting extant registration") 276 test.AssertEquals(t, clk.Now(), start) 277 test.AssertMetricWithLabelsEquals(t, sa.lagFactorCounter, prometheus.Labels{"method": "GetRegistration", "result": "notfound"}, 0) 278 279 _, err = sa.GetRegistration(ctx, &sapb.RegistrationID{Id: reg.Id + 1}) 280 test.AssertError(t, err, "selecting nonexistent registration") 281 test.AssertEquals(t, clk.Now(), start) 282 // With lagFactor disabled, we should never enter the retry codepath, as a 283 // result the metric should not increment. 284 test.AssertMetricWithLabelsEquals(t, sa.lagFactorCounter, prometheus.Labels{"method": "GetRegistration", "result": "notfound"}, 0) 285 286 // Now, set the lagFactor to 1. Trying to select a nonexistent registration 287 // should cause the clock to advance when GetRegistration sleeps and retries. 288 sa.lagFactor = 1 289 start = clk.Now() 290 291 _, err = sa.GetRegistration(ctx, &sapb.RegistrationID{Id: reg.Id}) 292 test.AssertNotError(t, err, "selecting extant registration") 293 test.AssertEquals(t, clk.Now(), start) 294 // lagFactor is enabled, but the registration exists. 295 test.AssertMetricWithLabelsEquals(t, sa.lagFactorCounter, prometheus.Labels{"method": "GetRegistration", "result": "notfound"}, 0) 296 297 _, err = sa.GetRegistration(ctx, &sapb.RegistrationID{Id: reg.Id + 1}) 298 test.AssertError(t, err, "selecting nonexistent registration") 299 test.AssertEquals(t, clk.Now(), start.Add(1)) 300 // With lagFactor enabled, we should enter the retry codepath and as a result 301 // the metric should increment. 302 test.AssertMetricWithLabelsEquals(t, sa.lagFactorCounter, prometheus.Labels{"method": "GetRegistration", "result": "notfound"}, 1) 303 } 304 305 // findIssuedName is a small helper test function to directly query the 306 // issuedNames table for a given name to find a serial (or return an err). 307 func findIssuedName(ctx context.Context, dbMap db.OneSelector, issuedName string) (string, error) { 308 var issuedNamesSerial string 309 err := dbMap.SelectOne( 310 ctx, 311 &issuedNamesSerial, 312 `SELECT serial FROM issuedNames 313 WHERE reversedName = ? 314 ORDER BY notBefore DESC 315 LIMIT 1`, 316 issuedName) 317 return issuedNamesSerial, err 318 } 319 320 func TestAddSerial(t *testing.T) { 321 sa, clk, cleanUp := initSA(t) 322 defer cleanUp() 323 324 reg := createWorkingRegistration(t, sa) 325 serial, testCert := test.ThrowAwayCert(t, clk) 326 327 _, err := sa.AddSerial(context.Background(), &sapb.AddSerialRequest{ 328 RegID: reg.Id, 329 Created: timestamppb.New(testCert.NotBefore), 330 Expires: timestamppb.New(testCert.NotAfter), 331 }) 332 test.AssertError(t, err, "adding without serial should fail") 333 334 _, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{ 335 Serial: serial, 336 Created: timestamppb.New(testCert.NotBefore), 337 Expires: timestamppb.New(testCert.NotAfter), 338 }) 339 test.AssertError(t, err, "adding without regid should fail") 340 341 _, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{ 342 Serial: serial, 343 RegID: reg.Id, 344 Expires: timestamppb.New(testCert.NotAfter), 345 }) 346 test.AssertError(t, err, "adding without created should fail") 347 348 _, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{ 349 Serial: serial, 350 RegID: reg.Id, 351 Created: timestamppb.New(testCert.NotBefore), 352 }) 353 test.AssertError(t, err, "adding without expires should fail") 354 355 _, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{ 356 Serial: serial, 357 RegID: reg.Id, 358 Created: timestamppb.New(testCert.NotBefore), 359 Expires: timestamppb.New(testCert.NotAfter), 360 }) 361 test.AssertNotError(t, err, "adding serial should have succeeded") 362 } 363 364 func TestGetSerialMetadata(t *testing.T) { 365 sa, clk, cleanUp := initSA(t) 366 defer cleanUp() 367 368 reg := createWorkingRegistration(t, sa) 369 serial, _ := test.ThrowAwayCert(t, clk) 370 371 _, err := sa.GetSerialMetadata(context.Background(), &sapb.Serial{Serial: serial}) 372 test.AssertError(t, err, "getting nonexistent serial should have failed") 373 374 now := clk.Now() 375 hourLater := now.Add(time.Hour) 376 _, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{ 377 Serial: serial, 378 RegID: reg.Id, 379 Created: timestamppb.New(now), 380 Expires: timestamppb.New(hourLater), 381 }) 382 test.AssertNotError(t, err, "failed to add test serial") 383 384 m, err := sa.GetSerialMetadata(context.Background(), &sapb.Serial{Serial: serial}) 385 386 test.AssertNotError(t, err, "getting serial should have succeeded") 387 test.AssertEquals(t, m.Serial, serial) 388 test.AssertEquals(t, m.RegistrationID, reg.Id) 389 test.AssertEquals(t, now, timestamppb.New(now).AsTime()) 390 test.AssertEquals(t, m.Expires.AsTime(), timestamppb.New(hourLater).AsTime()) 391 } 392 393 func TestAddPrecertificate(t *testing.T) { 394 ctx := context.Background() 395 sa, clk, cleanUp := initSA(t) 396 defer cleanUp() 397 398 reg := createWorkingRegistration(t, sa) 399 400 // Create a throw-away self signed certificate with a random name and 401 // serial number 402 serial, testCert := test.ThrowAwayCert(t, clk) 403 404 // Add the cert as a precertificate 405 regID := reg.Id 406 issuedTime := mustTimestamp("2018-04-01 07:00") 407 _, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ 408 Der: testCert.Raw, 409 RegID: regID, 410 Issued: issuedTime, 411 IssuerNameID: 1, 412 }) 413 test.AssertNotError(t, err, "Couldn't add test cert") 414 415 // It should have the expected certificate status 416 certStatus, err := sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial}) 417 test.AssertNotError(t, err, "Couldn't get status for test cert") 418 test.AssertEquals(t, certStatus.Status, string(core.OCSPStatusGood)) 419 now := clk.Now() 420 test.AssertEquals(t, now, certStatus.OcspLastUpdated.AsTime()) 421 422 // It should show up in the issued names table 423 issuedNamesSerial, err := findIssuedName(ctx, sa.dbMap, reverseFQDN(testCert.DNSNames[0])) 424 test.AssertNotError(t, err, "expected no err querying issuedNames for precert") 425 test.AssertEquals(t, issuedNamesSerial, serial) 426 427 // We should also be able to call AddCertificate with the same cert 428 // without it being an error. The duplicate err on inserting to 429 // issuedNames should be ignored. 430 _, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{ 431 Der: testCert.Raw, 432 RegID: regID, 433 Issued: issuedTime, 434 }) 435 test.AssertNotError(t, err, "unexpected err adding final cert after precert") 436 } 437 438 func TestAddPrecertificateNoOCSP(t *testing.T) { 439 sa, clk, cleanUp := initSA(t) 440 defer cleanUp() 441 442 reg := createWorkingRegistration(t, sa) 443 _, testCert := test.ThrowAwayCert(t, clk) 444 445 regID := reg.Id 446 issuedTime := mustTimestamp("2018-04-01 07:00") 447 _, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ 448 Der: testCert.Raw, 449 RegID: regID, 450 Issued: issuedTime, 451 IssuerNameID: 1, 452 }) 453 test.AssertNotError(t, err, "Couldn't add test cert") 454 } 455 456 func TestAddPreCertificateDuplicate(t *testing.T) { 457 sa, clk, cleanUp := initSA(t) 458 defer cleanUp() 459 460 reg := createWorkingRegistration(t, sa) 461 462 _, testCert := test.ThrowAwayCert(t, clk) 463 issuedTime := clk.Now() 464 465 _, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ 466 Der: testCert.Raw, 467 Issued: timestamppb.New(issuedTime), 468 RegID: reg.Id, 469 IssuerNameID: 1, 470 }) 471 test.AssertNotError(t, err, "Couldn't add test certificate") 472 473 _, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ 474 Der: testCert.Raw, 475 Issued: timestamppb.New(issuedTime), 476 RegID: reg.Id, 477 IssuerNameID: 1, 478 }) 479 test.AssertDeepEquals(t, err, berrors.DuplicateError("cannot add a duplicate cert")) 480 } 481 482 func TestAddPrecertificateIncomplete(t *testing.T) { 483 sa, clk, cleanUp := initSA(t) 484 defer cleanUp() 485 486 reg := createWorkingRegistration(t, sa) 487 488 // Create a throw-away self signed certificate with a random name and 489 // serial number 490 _, testCert := test.ThrowAwayCert(t, clk) 491 492 // Add the cert as a precertificate 493 regID := reg.Id 494 _, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ 495 Der: testCert.Raw, 496 RegID: regID, 497 Issued: mustTimestamp("2018-04-01 07:00"), 498 // Leaving out IssuerNameID 499 }) 500 501 test.AssertError(t, err, "Adding precert with no issuer did not fail") 502 } 503 504 func TestAddPrecertificateKeyHash(t *testing.T) { 505 sa, clk, cleanUp := initSA(t) 506 defer cleanUp() 507 reg := createWorkingRegistration(t, sa) 508 509 serial, testCert := test.ThrowAwayCert(t, clk) 510 _, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ 511 Der: testCert.Raw, 512 RegID: reg.Id, 513 Issued: timestamppb.New(testCert.NotBefore), 514 IssuerNameID: 1, 515 }) 516 test.AssertNotError(t, err, "failed to add precert") 517 518 var keyHashes []keyHashModel 519 _, err = sa.dbMap.Select(context.Background(), &keyHashes, "SELECT * FROM keyHashToSerial") 520 test.AssertNotError(t, err, "failed to retrieve rows from keyHashToSerial") 521 test.AssertEquals(t, len(keyHashes), 1) 522 test.AssertEquals(t, keyHashes[0].CertSerial, serial) 523 test.AssertEquals(t, keyHashes[0].CertNotAfter, testCert.NotAfter) 524 test.AssertEquals(t, keyHashes[0].CertNotAfter, timestamppb.New(testCert.NotAfter).AsTime()) 525 spkiHash := sha256.Sum256(testCert.RawSubjectPublicKeyInfo) 526 test.Assert(t, bytes.Equal(keyHashes[0].KeyHash, spkiHash[:]), "spki hash mismatch") 527 } 528 529 func TestAddCertificate(t *testing.T) { 530 sa, clk, cleanUp := initSA(t) 531 defer cleanUp() 532 533 reg := createWorkingRegistration(t, sa) 534 535 serial, testCert := test.ThrowAwayCert(t, clk) 536 537 issuedTime := sa.clk.Now() 538 _, err := sa.AddCertificate(ctx, &sapb.AddCertificateRequest{ 539 Der: testCert.Raw, 540 RegID: reg.Id, 541 Issued: timestamppb.New(issuedTime), 542 }) 543 test.AssertNotError(t, err, "Couldn't add test cert") 544 545 retrievedCert, err := sa.GetCertificate(ctx, &sapb.Serial{Serial: serial}) 546 test.AssertNotError(t, err, "Couldn't get test cert by full serial") 547 test.AssertByteEquals(t, testCert.Raw, retrievedCert.Der) 548 test.AssertEquals(t, retrievedCert.Issued.AsTime(), issuedTime) 549 550 // Calling AddCertificate with empty args should fail. 551 _, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{ 552 Der: nil, 553 RegID: reg.Id, 554 Issued: timestamppb.New(issuedTime), 555 }) 556 test.AssertError(t, err, "shouldn't be able to add cert with no DER") 557 _, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{ 558 Der: testCert.Raw, 559 RegID: 0, 560 Issued: timestamppb.New(issuedTime), 561 }) 562 test.AssertError(t, err, "shouldn't be able to add cert with no regID") 563 _, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{ 564 Der: testCert.Raw, 565 RegID: reg.Id, 566 Issued: nil, 567 }) 568 test.AssertError(t, err, "shouldn't be able to add cert with no issued timestamp") 569 _, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{ 570 Der: testCert.Raw, 571 RegID: reg.Id, 572 Issued: timestamppb.New(time.Time{}), 573 }) 574 test.AssertError(t, err, "shouldn't be able to add cert with zero issued timestamp") 575 } 576 577 func TestAddCertificateDuplicate(t *testing.T) { 578 sa, clk, cleanUp := initSA(t) 579 defer cleanUp() 580 581 reg := createWorkingRegistration(t, sa) 582 583 _, testCert := test.ThrowAwayCert(t, clk) 584 585 issuedTime := clk.Now() 586 _, err := sa.AddCertificate(ctx, &sapb.AddCertificateRequest{ 587 Der: testCert.Raw, 588 RegID: reg.Id, 589 Issued: timestamppb.New(issuedTime), 590 }) 591 test.AssertNotError(t, err, "Couldn't add test certificate") 592 593 _, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{ 594 Der: testCert.Raw, 595 RegID: reg.Id, 596 Issued: timestamppb.New(issuedTime), 597 }) 598 test.AssertDeepEquals(t, err, berrors.DuplicateError("cannot add a duplicate cert")) 599 600 } 601 602 func TestFQDNSetTimestampsForWindow(t *testing.T) { 603 sa, fc, cleanUp := initSA(t) 604 defer cleanUp() 605 606 tx, err := sa.dbMap.BeginTx(ctx) 607 test.AssertNotError(t, err, "Failed to open transaction") 608 609 idents := identifier.ACMEIdentifiers{ 610 identifier.NewDNS("a.example.com"), 611 identifier.NewDNS("B.example.com"), 612 } 613 614 // Invalid Window 615 req := &sapb.CountFQDNSetsRequest{ 616 Identifiers: idents.ToProtoSlice(), 617 Window: nil, 618 } 619 _, err = sa.FQDNSetTimestampsForWindow(ctx, req) 620 test.AssertErrorIs(t, err, errIncompleteRequest) 621 622 window := time.Hour * 3 623 req = &sapb.CountFQDNSetsRequest{ 624 Identifiers: idents.ToProtoSlice(), 625 Window: durationpb.New(window), 626 } 627 628 // Ensure zero issuance has occurred for names. 629 resp, err := sa.FQDNSetTimestampsForWindow(ctx, req) 630 test.AssertNotError(t, err, "Failed to count name sets") 631 test.AssertEquals(t, len(resp.Timestamps), 0) 632 633 // Add an issuance for names inside the window. 634 expires := fc.Now().Add(time.Hour * 2).UTC() 635 firstIssued := fc.Now() 636 err = addFQDNSet(ctx, tx, idents, "serial", firstIssued, expires) 637 test.AssertNotError(t, err, "Failed to add name set") 638 test.AssertNotError(t, tx.Commit(), "Failed to commit transaction") 639 640 // Ensure there's 1 issuance timestamp for names inside the window. 641 resp, err = sa.FQDNSetTimestampsForWindow(ctx, req) 642 test.AssertNotError(t, err, "Failed to count name sets") 643 test.AssertEquals(t, len(resp.Timestamps), 1) 644 test.AssertEquals(t, firstIssued, resp.Timestamps[len(resp.Timestamps)-1].AsTime()) 645 646 // Ensure that the hash isn't affected by changing name order/casing. 647 req.Identifiers = []*corepb.Identifier{ 648 identifier.NewDNS("b.example.com").ToProto(), 649 identifier.NewDNS("A.example.COM").ToProto(), 650 } 651 resp, err = sa.FQDNSetTimestampsForWindow(ctx, req) 652 test.AssertNotError(t, err, "Failed to count name sets") 653 test.AssertEquals(t, len(resp.Timestamps), 1) 654 test.AssertEquals(t, firstIssued, resp.Timestamps[len(resp.Timestamps)-1].AsTime()) 655 656 // Add another issuance for names inside the window. 657 tx, err = sa.dbMap.BeginTx(ctx) 658 test.AssertNotError(t, err, "Failed to open transaction") 659 err = addFQDNSet(ctx, tx, idents, "anotherSerial", firstIssued, expires) 660 test.AssertNotError(t, err, "Failed to add name set") 661 test.AssertNotError(t, tx.Commit(), "Failed to commit transaction") 662 663 // Ensure there are two issuance timestamps for names inside the window. 664 req.Identifiers = idents.ToProtoSlice() 665 resp, err = sa.FQDNSetTimestampsForWindow(ctx, req) 666 test.AssertNotError(t, err, "Failed to count name sets") 667 test.AssertEquals(t, len(resp.Timestamps), 2) 668 test.AssertEquals(t, firstIssued, resp.Timestamps[len(resp.Timestamps)-1].AsTime()) 669 670 // Add another issuance for names but just outside the window. 671 tx, err = sa.dbMap.BeginTx(ctx) 672 test.AssertNotError(t, err, "Failed to open transaction") 673 err = addFQDNSet(ctx, tx, idents, "yetAnotherSerial", firstIssued.Add(-window), expires) 674 test.AssertNotError(t, err, "Failed to add name set") 675 test.AssertNotError(t, tx.Commit(), "Failed to commit transaction") 676 677 // Ensure there are still only two issuance timestamps in the window. 678 resp, err = sa.FQDNSetTimestampsForWindow(ctx, req) 679 test.AssertNotError(t, err, "Failed to count name sets") 680 test.AssertEquals(t, len(resp.Timestamps), 2) 681 test.AssertEquals(t, firstIssued, resp.Timestamps[len(resp.Timestamps)-1].AsTime()) 682 683 resp, err = sa.FQDNSetTimestampsForWindow(ctx, &sapb.CountFQDNSetsRequest{ 684 Identifiers: idents.ToProtoSlice(), 685 Window: durationpb.New(window), 686 Limit: 1, 687 }) 688 test.AssertNotError(t, err, "Failed to count name sets") 689 test.AssertEquals(t, len(resp.Timestamps), 1) 690 test.AssertEquals(t, firstIssued, resp.Timestamps[len(resp.Timestamps)-1].AsTime()) 691 } 692 693 func TestFQDNSetExists(t *testing.T) { 694 sa, fc, cleanUp := initSA(t) 695 defer cleanUp() 696 697 idents := identifier.ACMEIdentifiers{ 698 identifier.NewDNS("a.example.com"), 699 identifier.NewDNS("B.example.com"), 700 } 701 702 exists, err := sa.FQDNSetExists(ctx, &sapb.FQDNSetExistsRequest{Identifiers: idents.ToProtoSlice()}) 703 test.AssertNotError(t, err, "Failed to check FQDN set existence") 704 test.Assert(t, !exists.Exists, "FQDN set shouldn't exist") 705 706 tx, err := sa.dbMap.BeginTx(ctx) 707 test.AssertNotError(t, err, "Failed to open transaction") 708 expires := fc.Now().Add(time.Hour * 2).UTC() 709 issued := fc.Now() 710 err = addFQDNSet(ctx, tx, idents, "serial", issued, expires) 711 test.AssertNotError(t, err, "Failed to add name set") 712 test.AssertNotError(t, tx.Commit(), "Failed to commit transaction") 713 714 exists, err = sa.FQDNSetExists(ctx, &sapb.FQDNSetExistsRequest{Identifiers: idents.ToProtoSlice()}) 715 test.AssertNotError(t, err, "Failed to check FQDN set existence") 716 test.Assert(t, exists.Exists, "FQDN set does exist") 717 } 718 719 type execRecorder struct { 720 valuesPerRow int 721 query string 722 args []any 723 } 724 725 func (e *execRecorder) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) { 726 e.query = query 727 e.args = args 728 return rowsResult{int64(len(args) / e.valuesPerRow)}, nil 729 } 730 731 type rowsResult struct { 732 rowsAffected int64 733 } 734 735 func (r rowsResult) LastInsertId() (int64, error) { 736 return r.rowsAffected, nil 737 } 738 739 func (r rowsResult) RowsAffected() (int64, error) { 740 return r.rowsAffected, nil 741 } 742 743 func TestAddIssuedNames(t *testing.T) { 744 serial := big.NewInt(1) 745 expectedSerial := "000000000000000000000000000000000001" 746 notBefore := mustTime("2018-02-14 12:00") 747 expectedNotBefore := notBefore.Truncate(24 * time.Hour) 748 placeholdersPerName := "(?,?,?,?)" 749 baseQuery := "INSERT INTO issuedNames (reversedName,serial,notBefore,renewal) VALUES" 750 751 testCases := []struct { 752 Name string 753 IssuedNames []string 754 SerialNumber *big.Int 755 NotBefore time.Time 756 Renewal bool 757 ExpectedArgs []any 758 }{ 759 { 760 Name: "One domain, not a renewal", 761 IssuedNames: []string{"example.co.uk"}, 762 SerialNumber: serial, 763 NotBefore: notBefore, 764 Renewal: false, 765 ExpectedArgs: []any{ 766 "uk.co.example", 767 expectedSerial, 768 expectedNotBefore, 769 false, 770 }, 771 }, 772 { 773 Name: "Two domains, not a renewal", 774 IssuedNames: []string{"example.co.uk", "example.xyz"}, 775 SerialNumber: serial, 776 NotBefore: notBefore, 777 Renewal: false, 778 ExpectedArgs: []any{ 779 "uk.co.example", 780 expectedSerial, 781 expectedNotBefore, 782 false, 783 "xyz.example", 784 expectedSerial, 785 expectedNotBefore, 786 false, 787 }, 788 }, 789 { 790 Name: "One domain, renewal", 791 IssuedNames: []string{"example.co.uk"}, 792 SerialNumber: serial, 793 NotBefore: notBefore, 794 Renewal: true, 795 ExpectedArgs: []any{ 796 "uk.co.example", 797 expectedSerial, 798 expectedNotBefore, 799 true, 800 }, 801 }, 802 { 803 Name: "Two domains, renewal", 804 IssuedNames: []string{"example.co.uk", "example.xyz"}, 805 SerialNumber: serial, 806 NotBefore: notBefore, 807 Renewal: true, 808 ExpectedArgs: []any{ 809 "uk.co.example", 810 expectedSerial, 811 expectedNotBefore, 812 true, 813 "xyz.example", 814 expectedSerial, 815 expectedNotBefore, 816 true, 817 }, 818 }, 819 } 820 821 for _, tc := range testCases { 822 t.Run(tc.Name, func(t *testing.T) { 823 e := execRecorder{valuesPerRow: 4} 824 err := addIssuedNames( 825 ctx, 826 &e, 827 &x509.Certificate{ 828 DNSNames: tc.IssuedNames, 829 SerialNumber: tc.SerialNumber, 830 NotBefore: tc.NotBefore, 831 }, 832 tc.Renewal) 833 test.AssertNotError(t, err, "addIssuedNames failed") 834 expectedPlaceholders := placeholdersPerName 835 for range len(tc.IssuedNames) - 1 { 836 expectedPlaceholders = fmt.Sprintf("%s,%s", expectedPlaceholders, placeholdersPerName) 837 } 838 expectedQuery := fmt.Sprintf("%s %s", baseQuery, expectedPlaceholders) 839 test.AssertEquals(t, e.query, expectedQuery) 840 if !reflect.DeepEqual(e.args, tc.ExpectedArgs) { 841 t.Errorf("Wrong args: got\n%#v, expected\n%#v", e.args, tc.ExpectedArgs) 842 } 843 }) 844 } 845 } 846 847 func TestDeactivateAuthorization2(t *testing.T) { 848 sa, fc, cleanUp := initSA(t) 849 defer cleanUp() 850 851 reg := createWorkingRegistration(t, sa) 852 853 // deactivate a pending authorization 854 expires := fc.Now().Add(time.Hour).UTC() 855 attemptedAt := fc.Now() 856 authzID := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expires) 857 _, err := sa.DeactivateAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID}) 858 test.AssertNotError(t, err, "sa.DeactivateAuthorization2 failed") 859 860 // deactivate a valid authorization 861 authzID = createFinalizedAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expires, "valid", attemptedAt) 862 _, err = sa.DeactivateAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID}) 863 test.AssertNotError(t, err, "sa.DeactivateAuthorization2 failed") 864 } 865 866 func TestDeactivateAccount(t *testing.T) { 867 sa, _, cleanUp := initSA(t) 868 defer cleanUp() 869 870 reg := createWorkingRegistration(t, sa) 871 872 // An incomplete request should be rejected. 873 _, err := sa.DeactivateRegistration(context.Background(), &sapb.RegistrationID{}) 874 test.AssertError(t, err, "Incomplete request should fail") 875 test.AssertContains(t, err.Error(), "incomplete") 876 877 // Deactivating should work, and return the same account but with updated 878 // status and cleared contacts. 879 got, err := sa.DeactivateRegistration(context.Background(), &sapb.RegistrationID{Id: reg.Id}) 880 test.AssertNotError(t, err, "DeactivateRegistration failed") 881 test.AssertEquals(t, got.Id, reg.Id) 882 test.AssertEquals(t, core.AcmeStatus(got.Status), core.StatusDeactivated) 883 884 // Double-check that the DeactivateRegistration method returned the right 885 // thing, by fetching the same account ourselves. 886 got, err = sa.GetRegistration(context.Background(), &sapb.RegistrationID{Id: reg.Id}) 887 test.AssertNotError(t, err, "GetRegistration failed") 888 test.AssertEquals(t, got.Id, reg.Id) 889 test.AssertEquals(t, core.AcmeStatus(got.Status), core.StatusDeactivated) 890 891 // Attempting to deactivate it a second time should fail, since it is already 892 // deactivated. 893 _, err = sa.DeactivateRegistration(context.Background(), &sapb.RegistrationID{Id: reg.Id}) 894 test.AssertError(t, err, "Deactivating an already-deactivated account should fail") 895 } 896 897 func TestReverseFQDN(t *testing.T) { 898 testCases := []struct { 899 fqdn string 900 reversed string 901 }{ 902 {"", ""}, 903 {"...", "..."}, 904 {"com", "com"}, 905 {"example.com", "com.example"}, 906 {"www.example.com", "com.example.www"}, 907 {"world.wide.web.example.com", "com.example.web.wide.world"}, 908 } 909 910 for _, tc := range testCases { 911 output := reverseFQDN(tc.fqdn) 912 test.AssertEquals(t, output, tc.reversed) 913 914 output = reverseFQDN(tc.reversed) 915 test.AssertEquals(t, output, tc.fqdn) 916 } 917 } 918 919 func TestEncodeIssuedName(t *testing.T) { 920 testCases := []struct { 921 issuedName string 922 reversed string 923 oneWay bool 924 }{ 925 // Empty strings and bare separators/TLDs should be unchanged. 926 {"", "", false}, 927 {"...", "...", false}, 928 {"com", "com", false}, 929 // FQDNs should be reversed. 930 {"example.com", "com.example", false}, 931 {"www.example.com", "com.example.www", false}, 932 {"world.wide.web.example.com", "com.example.web.wide.world", false}, 933 // IP addresses should stay the same. 934 {"1.2.3.4", "1.2.3.4", false}, 935 {"2602:ff3a:1:abad:c0f:fee:abad:cafe", "2602:ff3a:1:abad:c0f:fee:abad:cafe", false}, 936 // Tricksy FQDNs that look like IPv6 addresses should be parsed as FQDNs. 937 {"2602.ff3a.1.abad.c0f.fee.abad.cafe", "cafe.abad.fee.c0f.abad.1.ff3a.2602", false}, 938 {"2602.ff3a.0001.abad.0c0f.0fee.abad.cafe", "cafe.abad.0fee.0c0f.abad.0001.ff3a.2602", false}, 939 // IPv6 addresses should be returned in RFC 5952 format. 940 {"2602:ff3a:0001:abad:0c0f:0fee:abad:cafe", "2602:ff3a:1:abad:c0f:fee:abad:cafe", true}, 941 } 942 943 for _, tc := range testCases { 944 output := EncodeIssuedName(tc.issuedName) 945 test.AssertEquals(t, output, tc.reversed) 946 947 if !tc.oneWay { 948 output = EncodeIssuedName(tc.reversed) 949 test.AssertEquals(t, output, tc.issuedName) 950 } 951 } 952 } 953 954 func TestNewOrderAndAuthzs(t *testing.T) { 955 sa, _, cleanup := initSA(t) 956 defer cleanup() 957 958 reg := createWorkingRegistration(t, sa) 959 960 // Insert two pre-existing authorizations to reference 961 idA := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("a.com"), sa.clk.Now().Add(time.Hour)) 962 idB := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("b.com"), sa.clk.Now().Add(time.Hour)) 963 964 nowC := sa.clk.Now().Add(time.Hour) 965 nowD := sa.clk.Now().Add(time.Hour) 966 expires := sa.clk.Now().Add(2 * time.Hour) 967 req := &sapb.NewOrderAndAuthzsRequest{ 968 // Insert an order for four names, two of which already have authzs 969 NewOrder: &sapb.NewOrderRequest{ 970 RegistrationID: reg.Id, 971 Expires: timestamppb.New(expires), 972 Identifiers: []*corepb.Identifier{ 973 identifier.NewDNS("a.com").ToProto(), 974 identifier.NewDNS("b.com").ToProto(), 975 identifier.NewDNS("c.com").ToProto(), 976 identifier.NewDNS("d.com").ToProto(), 977 }, 978 V2Authorizations: []int64{idA, idB}, 979 }, 980 // And add new authorizations for the other two names. 981 NewAuthzs: []*sapb.NewAuthzRequest{ 982 { 983 Identifier: &corepb.Identifier{Type: "dns", Value: "c.com"}, 984 RegistrationID: reg.Id, 985 Expires: timestamppb.New(nowC), 986 ChallengeTypes: []string{string(core.ChallengeTypeHTTP01)}, 987 Token: core.NewToken(), 988 }, 989 { 990 Identifier: &corepb.Identifier{Type: "dns", Value: "d.com"}, 991 RegistrationID: reg.Id, 992 Expires: timestamppb.New(nowD), 993 ChallengeTypes: []string{string(core.ChallengeTypeHTTP01)}, 994 Token: core.NewToken(), 995 }, 996 }, 997 } 998 order, err := sa.NewOrderAndAuthzs(context.Background(), req) 999 test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed") 1000 test.Assert(t, order.Id != 0, "order ID should be non-zero") 1001 test.AssertEquals(t, len(order.V2Authorizations), 4) 1002 test.AssertSliceContains(t, order.V2Authorizations, idA) 1003 test.AssertSliceContains(t, order.V2Authorizations, idB) 1004 // Ensure that two new authzs were created. 1005 var newAuthzIDs []int64 1006 for _, id := range order.V2Authorizations { 1007 if id != idA && id != idB { 1008 newAuthzIDs = append(newAuthzIDs, id) 1009 } 1010 } 1011 test.AssertEquals(t, len(newAuthzIDs), 2) 1012 test.Assert(t, newAuthzIDs[0] != newAuthzIDs[1], "expected distinct new authz IDs") 1013 1014 var authzIDs []int64 1015 _, err = sa.dbMap.Select(ctx, &authzIDs, "SELECT authzID FROM orderToAuthz2 WHERE orderID = ?;", order.Id) 1016 test.AssertNotError(t, err, "Failed to count orderToAuthz entries") 1017 test.AssertEquals(t, len(authzIDs), 4) 1018 slices.Sort(authzIDs) 1019 expectedIDs := append([]int64{idA, idB}, newAuthzIDs...) 1020 slices.Sort(expectedIDs) 1021 test.AssertDeepEquals(t, authzIDs, expectedIDs) 1022 } 1023 1024 func TestNewOrderAndAuthzs_ReuseOnly(t *testing.T) { 1025 sa, fc, cleanup := initSA(t) 1026 defer cleanup() 1027 1028 reg := createWorkingRegistration(t, sa) 1029 expires := fc.Now().Add(2 * time.Hour) 1030 1031 // Insert two pre-existing authorizations to reference 1032 idA := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("a.com"), sa.clk.Now().Add(time.Hour)) 1033 idB := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("b.com"), sa.clk.Now().Add(time.Hour)) 1034 req := &sapb.NewOrderAndAuthzsRequest{ 1035 // Insert an order for four names, two of which already have authzs 1036 NewOrder: &sapb.NewOrderRequest{ 1037 RegistrationID: reg.Id, 1038 Expires: timestamppb.New(expires), 1039 Identifiers: []*corepb.Identifier{ 1040 identifier.NewDNS("a.com").ToProto(), 1041 identifier.NewDNS("b.com").ToProto(), 1042 }, 1043 V2Authorizations: []int64{idA, idB}, 1044 }, 1045 } 1046 order, err := sa.NewOrderAndAuthzs(context.Background(), req) 1047 if err != nil { 1048 t.Fatal("sa.NewOrderAndAuthzs:", err) 1049 } 1050 if !reflect.DeepEqual(order.V2Authorizations, []int64{idA, idB}) { 1051 t.Errorf("sa.NewOrderAndAuthzs().V2Authorizations: want [%d, %d], got %v", idA, idB, order.V2Authorizations) 1052 } 1053 } 1054 1055 func TestNewOrderAndAuthzs_CreateOnly(t *testing.T) { 1056 sa, fc, cleanup := initSA(t) 1057 defer cleanup() 1058 1059 reg := createWorkingRegistration(t, sa) 1060 expires := fc.Now().Add(2 * time.Hour) 1061 1062 // Insert two pre-existing authorizations to reference 1063 _ = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("a.com"), sa.clk.Now().Add(time.Hour)) 1064 _ = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("b.com"), sa.clk.Now().Add(time.Hour)) 1065 req := &sapb.NewOrderAndAuthzsRequest{ 1066 // Insert an order for four names, two of which already have authzs 1067 NewOrder: &sapb.NewOrderRequest{ 1068 RegistrationID: reg.Id, 1069 Expires: timestamppb.New(expires), 1070 Identifiers: []*corepb.Identifier{ 1071 identifier.NewDNS("a.com").ToProto(), 1072 identifier.NewDNS("b.com").ToProto(), 1073 }, 1074 }, 1075 NewAuthzs: []*sapb.NewAuthzRequest{ 1076 { 1077 Identifier: &corepb.Identifier{Type: "dns", Value: "a.com"}, 1078 RegistrationID: reg.Id, 1079 Expires: timestamppb.New(expires), 1080 ChallengeTypes: []string{string(core.ChallengeTypeDNS01)}, 1081 Token: core.NewToken(), 1082 }, 1083 }, 1084 } 1085 order, err := sa.NewOrderAndAuthzs(context.Background(), req) 1086 if err != nil { 1087 t.Fatal("sa.NewOrderAndAuthzs:", err) 1088 } 1089 if len(order.V2Authorizations) != 1 { 1090 t.Fatalf("len(sa.NewOrderAndAuthzs().V2Authorizations): want 1, got %v", len(order.V2Authorizations)) 1091 } 1092 gotAuthz, err := sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: order.V2Authorizations[0]}) 1093 if err != nil { 1094 t.Fatalf("retrieving inserted authz: %s", err) 1095 } 1096 if gotAuthz.Identifier.Value != "a.com" { 1097 t.Errorf("New order authz identifier = %v, want %v", gotAuthz.Identifier.Value, "a.com") 1098 } 1099 } 1100 1101 func TestNewOrderAndAuthzs_NoAuthzsError(t *testing.T) { 1102 sa, fc, cleanup := initSA(t) 1103 defer cleanup() 1104 1105 reg := createWorkingRegistration(t, sa) 1106 expires := fc.Now().Add(2 * time.Hour) 1107 1108 // Insert two pre-existing authorizations to reference 1109 req := &sapb.NewOrderAndAuthzsRequest{ 1110 // Insert an order for four names, two of which already have authzs 1111 NewOrder: &sapb.NewOrderRequest{ 1112 RegistrationID: reg.Id, 1113 Expires: timestamppb.New(expires), 1114 Identifiers: nil, 1115 }, 1116 NewAuthzs: nil, 1117 } 1118 _, err := sa.NewOrderAndAuthzs(context.Background(), req) 1119 if err != errIncompleteRequest { 1120 t.Errorf("sa.NewOrderAndAuthzs with no authzs: want %v, got %v", errIncompleteRequest, err) 1121 } 1122 } 1123 1124 // TestNewOrderAndAuthzs_NonNilInnerOrder verifies that a nil 1125 // sapb.NewOrderAndAuthzsRequest NewOrder object returns an error. 1126 func TestNewOrderAndAuthzs_NonNilInnerOrder(t *testing.T) { 1127 sa, fc, cleanup := initSA(t) 1128 defer cleanup() 1129 1130 reg := createWorkingRegistration(t, sa) 1131 1132 expires := fc.Now().Add(2 * time.Hour) 1133 _, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{ 1134 NewAuthzs: []*sapb.NewAuthzRequest{ 1135 { 1136 Identifier: &corepb.Identifier{Type: "dns", Value: "c.com"}, 1137 RegistrationID: reg.Id, 1138 Expires: timestamppb.New(expires), 1139 ChallengeTypes: []string{string(core.ChallengeTypeDNS01)}, 1140 Token: core.NewToken(), 1141 }, 1142 }, 1143 }) 1144 test.AssertErrorIs(t, err, errIncompleteRequest) 1145 } 1146 1147 func TestNewOrderAndAuthzs_MismatchedRegID(t *testing.T) { 1148 sa, _, cleanup := initSA(t) 1149 defer cleanup() 1150 1151 _, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{ 1152 NewOrder: &sapb.NewOrderRequest{ 1153 RegistrationID: 1, 1154 }, 1155 NewAuthzs: []*sapb.NewAuthzRequest{ 1156 { 1157 RegistrationID: 2, 1158 }, 1159 }, 1160 }) 1161 test.AssertError(t, err, "mismatched regIDs should fail") 1162 test.AssertContains(t, err.Error(), "same account") 1163 } 1164 1165 func TestNewOrderAndAuthzs_NewAuthzExpectedFields(t *testing.T) { 1166 sa, fc, cleanup := initSA(t) 1167 defer cleanup() 1168 1169 reg := createWorkingRegistration(t, sa) 1170 expires := fc.Now().Add(time.Hour) 1171 domain := "a.com" 1172 1173 // Create an authz that does not yet exist in the database with some invalid 1174 // data smuggled in. 1175 order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{ 1176 NewAuthzs: []*sapb.NewAuthzRequest{ 1177 { 1178 Identifier: &corepb.Identifier{Type: "dns", Value: domain}, 1179 RegistrationID: reg.Id, 1180 Expires: timestamppb.New(expires), 1181 ChallengeTypes: []string{string(core.ChallengeTypeHTTP01)}, 1182 Token: core.NewToken(), 1183 }, 1184 }, 1185 NewOrder: &sapb.NewOrderRequest{ 1186 RegistrationID: reg.Id, 1187 Expires: timestamppb.New(expires), 1188 Identifiers: []*corepb.Identifier{identifier.NewDNS(domain).ToProto()}, 1189 }, 1190 }) 1191 test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed") 1192 1193 // Safely get the authz for the order we created above. 1194 obj, err := sa.dbReadOnlyMap.Get(ctx, authzModel{}, order.V2Authorizations[0]) 1195 test.AssertNotError(t, err, fmt.Sprintf("authorization %d not found", order.V2Authorizations[0])) 1196 1197 // To access the data stored in obj at compile time, we type assert obj 1198 // into a pointer to an authzModel. 1199 am, ok := obj.(*authzModel) 1200 test.Assert(t, ok, "Could not type assert obj into authzModel") 1201 1202 // If we're making a brand new authz, it should have the pending status 1203 // regardless of what incorrect status value was passed in during construction. 1204 test.AssertEquals(t, am.Status, statusUint(core.StatusPending)) 1205 1206 // Testing for the existence of these boxed nils is a definite break from 1207 // our paradigm of avoiding passing around boxed nils whenever possible. 1208 // However, the existence of these boxed nils in relation to this test is 1209 // actually expected. If these tests fail, then a possible SA refactor or RA 1210 // bug placed incorrect data into brand new authz input fields. 1211 test.AssertBoxedNil(t, am.Attempted, "am.Attempted should be nil") 1212 test.AssertBoxedNil(t, am.AttemptedAt, "am.AttemptedAt should be nil") 1213 test.AssertBoxedNil(t, am.ValidationError, "am.ValidationError should be nil") 1214 test.AssertBoxedNil(t, am.ValidationRecord, "am.ValidationRecord should be nil") 1215 } 1216 1217 func TestNewOrderAndAuthzs_Profile(t *testing.T) { 1218 sa, fc, cleanup := initSA(t) 1219 defer cleanup() 1220 1221 reg := createWorkingRegistration(t, sa) 1222 expires := fc.Now().Add(time.Hour) 1223 1224 // Create and order and authz while specifying a profile. 1225 order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{ 1226 NewOrder: &sapb.NewOrderRequest{ 1227 RegistrationID: reg.Id, 1228 Expires: timestamppb.New(expires), 1229 Identifiers: []*corepb.Identifier{identifier.NewDNS("example.com").ToProto()}, 1230 CertificateProfileName: "test", 1231 }, 1232 NewAuthzs: []*sapb.NewAuthzRequest{ 1233 { 1234 Identifier: &corepb.Identifier{Type: "dns", Value: "example.com"}, 1235 RegistrationID: reg.Id, 1236 Expires: timestamppb.New(expires), 1237 ChallengeTypes: []string{string(core.ChallengeTypeHTTP01)}, 1238 Token: core.NewToken(), 1239 }, 1240 }, 1241 }) 1242 if err != nil { 1243 t.Fatalf("inserting order and authzs: %s", err) 1244 } 1245 1246 // Retrieve the order and check that the profile is correct. 1247 gotOrder, err := sa.GetOrder(context.Background(), &sapb.OrderRequest{Id: order.Id}) 1248 if err != nil { 1249 t.Fatalf("retrieving inserted order: %s", err) 1250 } 1251 if gotOrder.CertificateProfileName != "test" { 1252 t.Errorf("order.CertificateProfileName = %v, want %v", gotOrder.CertificateProfileName, "test") 1253 } 1254 1255 // Retrieve the authz and check that the profile is correct. 1256 // Safely get the authz for the order we created above. 1257 gotAuthz, err := sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: order.V2Authorizations[0]}) 1258 if err != nil { 1259 t.Fatalf("retrieving inserted authz: %s", err) 1260 } 1261 if gotAuthz.CertificateProfileName != "test" { 1262 t.Errorf("authz.CertificateProfileName = %v, want %v", gotAuthz.CertificateProfileName, "test") 1263 } 1264 } 1265 1266 func TestSetOrderProcessing(t *testing.T) { 1267 sa, fc, cleanup := initSA(t) 1268 defer cleanup() 1269 1270 reg := createWorkingRegistration(t, sa) 1271 1272 // Add one valid authz 1273 expires := fc.Now().Add(time.Hour) 1274 attemptedAt := fc.Now() 1275 authzID := createFinalizedAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expires, "valid", attemptedAt) 1276 1277 // Add a new order in pending status with no certificate serial 1278 expires1Year := sa.clk.Now().Add(365 * 24 * time.Hour) 1279 order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{ 1280 NewOrder: &sapb.NewOrderRequest{ 1281 RegistrationID: reg.Id, 1282 Expires: timestamppb.New(expires1Year), 1283 Identifiers: []*corepb.Identifier{identifier.NewDNS("example.com").ToProto()}, 1284 V2Authorizations: []int64{authzID}, 1285 }, 1286 }) 1287 test.AssertNotError(t, err, "NewOrderAndAuthzs failed") 1288 1289 // Set the order to be processing 1290 _, err = sa.SetOrderProcessing(context.Background(), &sapb.OrderRequest{Id: order.Id}) 1291 test.AssertNotError(t, err, "SetOrderProcessing failed") 1292 1293 // Read the order by ID from the DB to check the status was correctly updated 1294 // to processing 1295 updatedOrder, err := sa.GetOrder( 1296 context.Background(), 1297 &sapb.OrderRequest{Id: order.Id}) 1298 test.AssertNotError(t, err, "GetOrder failed") 1299 test.AssertEquals(t, updatedOrder.Status, string(core.StatusProcessing)) 1300 test.AssertEquals(t, updatedOrder.BeganProcessing, true) 1301 1302 // Try to set the same order to be processing again. We should get an error. 1303 _, err = sa.SetOrderProcessing(context.Background(), &sapb.OrderRequest{Id: order.Id}) 1304 test.AssertError(t, err, "Set the same order processing twice. This should have been an error.") 1305 test.AssertErrorIs(t, err, berrors.OrderNotReady) 1306 } 1307 1308 func TestFinalizeOrder(t *testing.T) { 1309 sa, fc, cleanup := initSA(t) 1310 defer cleanup() 1311 1312 reg := createWorkingRegistration(t, sa) 1313 expires := fc.Now().Add(time.Hour) 1314 attemptedAt := fc.Now() 1315 authzID := createFinalizedAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expires, "valid", attemptedAt) 1316 1317 // Add a new order in pending status with no certificate serial 1318 expires1Year := sa.clk.Now().Add(365 * 24 * time.Hour) 1319 order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{ 1320 NewOrder: &sapb.NewOrderRequest{ 1321 RegistrationID: reg.Id, 1322 Expires: timestamppb.New(expires1Year), 1323 Identifiers: []*corepb.Identifier{identifier.NewDNS("example.com").ToProto()}, 1324 V2Authorizations: []int64{authzID}, 1325 }, 1326 }) 1327 test.AssertNotError(t, err, "NewOrderAndAuthzs failed") 1328 1329 // Set the order to processing so it can be finalized 1330 _, err = sa.SetOrderProcessing(ctx, &sapb.OrderRequest{Id: order.Id}) 1331 test.AssertNotError(t, err, "SetOrderProcessing failed") 1332 1333 // Finalize the order with a certificate serial 1334 order.CertificateSerial = "eat.serial.for.breakfast" 1335 _, err = sa.FinalizeOrder(context.Background(), &sapb.FinalizeOrderRequest{Id: order.Id, CertificateSerial: order.CertificateSerial}) 1336 test.AssertNotError(t, err, "FinalizeOrder failed") 1337 1338 // Read the order by ID from the DB to check the certificate serial and status 1339 // was correctly updated 1340 updatedOrder, err := sa.GetOrder( 1341 context.Background(), 1342 &sapb.OrderRequest{Id: order.Id}) 1343 test.AssertNotError(t, err, "GetOrder failed") 1344 test.AssertEquals(t, updatedOrder.CertificateSerial, "eat.serial.for.breakfast") 1345 test.AssertEquals(t, updatedOrder.Status, string(core.StatusValid)) 1346 } 1347 1348 // TestGetOrder tests that round-tripping a simple order through 1349 // NewOrderAndAuthzs and GetOrder has the expected result. 1350 func TestGetOrder(t *testing.T) { 1351 sa, fc, cleanup := initSA(t) 1352 defer cleanup() 1353 1354 reg := createWorkingRegistration(t, sa) 1355 ident := identifier.NewDNS("example.com") 1356 authzExpires := fc.Now().Add(time.Hour) 1357 authzID := createPendingAuthorization(t, sa, reg.Id, ident, authzExpires) 1358 1359 // Set the order to expire in two hours 1360 expires := fc.Now().Add(2 * time.Hour) 1361 1362 inputOrder := &corepb.Order{ 1363 RegistrationID: reg.Id, 1364 Expires: timestamppb.New(expires), 1365 Identifiers: []*corepb.Identifier{ident.ToProto()}, 1366 V2Authorizations: []int64{authzID}, 1367 } 1368 1369 // Create the order 1370 order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{ 1371 NewOrder: &sapb.NewOrderRequest{ 1372 RegistrationID: inputOrder.RegistrationID, 1373 Expires: inputOrder.Expires, 1374 Identifiers: inputOrder.Identifiers, 1375 V2Authorizations: inputOrder.V2Authorizations, 1376 }, 1377 }) 1378 test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed") 1379 1380 // Fetch the order by its ID and make sure it matches the expected 1381 storedOrder, err := sa.GetOrder(context.Background(), &sapb.OrderRequest{Id: order.Id}) 1382 test.AssertNotError(t, err, "sa.GetOrder failed") 1383 created := sa.clk.Now() 1384 test.AssertDeepEquals(t, storedOrder, &corepb.Order{ 1385 // The registration ID, authorizations, expiry, and identifiers should match the 1386 // input to NewOrderAndAuthzs 1387 RegistrationID: inputOrder.RegistrationID, 1388 V2Authorizations: inputOrder.V2Authorizations, 1389 Identifiers: inputOrder.Identifiers, 1390 Expires: inputOrder.Expires, 1391 // The ID should have been set to 1 by the SA 1392 Id: storedOrder.Id, 1393 // The status should be pending 1394 Status: string(core.StatusPending), 1395 // The serial should be empty since this is a pending order 1396 CertificateSerial: "", 1397 // We should not be processing it 1398 BeganProcessing: false, 1399 // The created timestamp should have been set to the current time 1400 Created: timestamppb.New(created), 1401 }) 1402 1403 if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" { 1404 // A migration is needed before initializing an SA with StoreAuthzsInOrders 1405 return 1406 } 1407 1408 features.Set(features.Config{ 1409 StoreAuthzsInOrders: false, 1410 }) 1411 1412 dbMap, err := DBMapForTest(vars.DBConnSA) 1413 if err != nil { 1414 t.Fatalf("Failed to create dbMap: %s", err) 1415 } 1416 1417 dbIncidentsMap, err := DBMapForTest(vars.DBConnIncidents) 1418 if err != nil { 1419 t.Fatalf("Failed to create dbMap: %s", err) 1420 } 1421 1422 sa2, err := NewSQLStorageAuthorityRO(dbMap, dbIncidentsMap, metrics.NoopRegisterer, 1, 0, fc, log) 1423 if err != nil { 1424 t.Fatalf("Failed to create SA: %s", err) 1425 } 1426 1427 storedOrder2, err := sa2.GetOrder(context.Background(), &sapb.OrderRequest{Id: order.Id}) 1428 test.AssertNotError(t, err, "fetching order after enabling StoreAuthzsInOrders") 1429 test.AssertDeepEquals(t, storedOrder2, storedOrder) 1430 } 1431 1432 // TestGetOrderWithProfile tests that round-tripping a simple order through 1433 // NewOrderAndAuthzs and GetOrder has the expected result. 1434 func TestGetOrderWithProfile(t *testing.T) { 1435 sa, fc, cleanup := initSA(t) 1436 defer cleanup() 1437 1438 reg := createWorkingRegistration(t, sa) 1439 ident := identifier.NewDNS("example.com") 1440 authzExpires := fc.Now().Add(time.Hour) 1441 authzID := createPendingAuthorization(t, sa, reg.Id, ident, authzExpires) 1442 1443 // Set the order to expire in two hours 1444 expires := fc.Now().Add(2 * time.Hour) 1445 1446 inputOrder := &corepb.Order{ 1447 RegistrationID: reg.Id, 1448 Expires: timestamppb.New(expires), 1449 Identifiers: []*corepb.Identifier{ident.ToProto()}, 1450 V2Authorizations: []int64{authzID}, 1451 CertificateProfileName: "tbiapb", 1452 } 1453 1454 // Create the order 1455 order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{ 1456 NewOrder: &sapb.NewOrderRequest{ 1457 RegistrationID: inputOrder.RegistrationID, 1458 Expires: inputOrder.Expires, 1459 Identifiers: inputOrder.Identifiers, 1460 V2Authorizations: inputOrder.V2Authorizations, 1461 CertificateProfileName: inputOrder.CertificateProfileName, 1462 }, 1463 }) 1464 test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed") 1465 1466 // Fetch the order by its ID and make sure it matches the expected 1467 storedOrder, err := sa.GetOrder(context.Background(), &sapb.OrderRequest{Id: order.Id}) 1468 test.AssertNotError(t, err, "sa.GetOrder failed") 1469 created := sa.clk.Now() 1470 test.AssertDeepEquals(t, storedOrder, &corepb.Order{ 1471 // The registration ID, authorizations, expiry, and names should match the 1472 // input to NewOrderAndAuthzs 1473 RegistrationID: inputOrder.RegistrationID, 1474 V2Authorizations: inputOrder.V2Authorizations, 1475 Identifiers: inputOrder.Identifiers, 1476 Expires: inputOrder.Expires, 1477 // The ID should have been set to 1 by the SA 1478 Id: storedOrder.Id, 1479 // The status should be pending 1480 Status: string(core.StatusPending), 1481 // The serial should be empty since this is a pending order 1482 CertificateSerial: "", 1483 // We should not be processing it 1484 BeganProcessing: false, 1485 // The created timestamp should have been set to the current time 1486 Created: timestamppb.New(created), 1487 CertificateProfileName: "tbiapb", 1488 }) 1489 } 1490 1491 // TestGetAuthorization2NoRows ensures that the GetAuthorization2 function returns 1492 // the correct error when there are no results for the provided ID. 1493 func TestGetAuthorization2NoRows(t *testing.T) { 1494 sa, _, cleanUp := initSA(t) 1495 defer cleanUp() 1496 1497 // An empty authz ID should result in a not found berror. 1498 id := int64(123) 1499 _, err := sa.GetAuthorization2(ctx, &sapb.AuthorizationID2{Id: id}) 1500 test.AssertError(t, err, "Didn't get an error looking up non-existent authz ID") 1501 test.AssertErrorIs(t, err, berrors.NotFound) 1502 } 1503 1504 func TestFasterGetOrderForNames(t *testing.T) { 1505 sa, fc, cleanUp := initSA(t) 1506 defer cleanUp() 1507 1508 ident := identifier.NewDNS("example.com") 1509 expires := fc.Now().Add(time.Hour) 1510 1511 key, _ := goodTestJWK().MarshalJSON() 1512 reg, err := sa.NewRegistration(ctx, &corepb.Registration{ 1513 Key: key, 1514 }) 1515 test.AssertNotError(t, err, "Couldn't create test registration") 1516 1517 authzIDs := createPendingAuthorization(t, sa, reg.Id, ident, expires) 1518 1519 _, err = sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{ 1520 NewOrder: &sapb.NewOrderRequest{ 1521 RegistrationID: reg.Id, 1522 Expires: timestamppb.New(expires), 1523 V2Authorizations: []int64{authzIDs}, 1524 Identifiers: []*corepb.Identifier{ident.ToProto()}, 1525 }, 1526 }) 1527 test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed") 1528 1529 _, err = sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{ 1530 NewOrder: &sapb.NewOrderRequest{ 1531 RegistrationID: reg.Id, 1532 Expires: timestamppb.New(expires), 1533 V2Authorizations: []int64{authzIDs}, 1534 Identifiers: []*corepb.Identifier{ident.ToProto()}, 1535 }, 1536 }) 1537 test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed") 1538 1539 _, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{ 1540 AcctID: reg.Id, 1541 Identifiers: []*corepb.Identifier{ident.ToProto()}, 1542 }) 1543 test.AssertNotError(t, err, "sa.GetOrderForNames failed") 1544 } 1545 1546 func TestGetOrderForNames(t *testing.T) { 1547 sa, fc, cleanUp := initSA(t) 1548 defer cleanUp() 1549 1550 // Give the order we create a short lifetime 1551 orderLifetime := time.Hour 1552 expires := fc.Now().Add(orderLifetime) 1553 1554 // Create two test registrations to associate with orders 1555 key, _ := goodTestJWK().MarshalJSON() 1556 regA, err := sa.NewRegistration(ctx, &corepb.Registration{ 1557 Key: key, 1558 }) 1559 test.AssertNotError(t, err, "Couldn't create test registration") 1560 1561 // Add one pending authz for the first name for regA and one 1562 // pending authz for the second name for regA 1563 authzExpires := fc.Now().Add(time.Hour) 1564 authzIDA := createPendingAuthorization(t, sa, regA.Id, identifier.NewDNS("example.com"), authzExpires) 1565 authzIDB := createPendingAuthorization(t, sa, regA.Id, identifier.NewDNS("just.another.example.com"), authzExpires) 1566 1567 ctx := context.Background() 1568 idents := identifier.ACMEIdentifiers{ 1569 identifier.NewDNS("example.com"), 1570 identifier.NewDNS("just.another.example.com"), 1571 } 1572 1573 // Call GetOrderForNames for a set of names we haven't created an order for 1574 // yet 1575 result, err := sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{ 1576 AcctID: regA.Id, 1577 Identifiers: idents.ToProtoSlice(), 1578 }) 1579 // We expect the result to return an error 1580 test.AssertError(t, err, "sa.GetOrderForNames did not return an error for an empty result") 1581 // The error should be a notfound error 1582 test.AssertErrorIs(t, err, berrors.NotFound) 1583 // The result should be nil 1584 test.Assert(t, result == nil, "sa.GetOrderForNames for non-existent order returned non-nil result") 1585 1586 // Add a new order for a set of names 1587 order, err := sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{ 1588 NewOrder: &sapb.NewOrderRequest{ 1589 RegistrationID: regA.Id, 1590 Expires: timestamppb.New(expires), 1591 V2Authorizations: []int64{authzIDA, authzIDB}, 1592 Identifiers: idents.ToProtoSlice(), 1593 }, 1594 }) 1595 // It shouldn't error 1596 test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed") 1597 // The order ID shouldn't be nil 1598 test.AssertNotNil(t, order.Id, "NewOrderAndAuthzs returned with a nil Id") 1599 1600 // Call GetOrderForNames with the same account ID and set of names as the 1601 // above NewOrderAndAuthzs call 1602 result, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{ 1603 AcctID: regA.Id, 1604 Identifiers: idents.ToProtoSlice(), 1605 }) 1606 // It shouldn't error 1607 test.AssertNotError(t, err, "sa.GetOrderForNames failed") 1608 // The order returned should have the same ID as the order we created above 1609 test.AssertNotNil(t, result, "Returned order was nil") 1610 test.AssertEquals(t, result.Id, order.Id) 1611 1612 // Call GetOrderForNames with a different account ID from the NewOrderAndAuthzs call 1613 regB := int64(1337) 1614 result, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{ 1615 AcctID: regB, 1616 Identifiers: idents.ToProtoSlice(), 1617 }) 1618 // It should error 1619 test.AssertError(t, err, "sa.GetOrderForNames did not return an error for an empty result") 1620 // The error should be a notfound error 1621 test.AssertErrorIs(t, err, berrors.NotFound) 1622 // The result should be nil 1623 test.Assert(t, result == nil, "sa.GetOrderForNames for diff AcctID returned non-nil result") 1624 1625 // Advance the clock beyond the initial order's lifetime 1626 fc.Add(2 * orderLifetime) 1627 1628 // Call GetOrderForNames again with the same account ID and set of names as 1629 // the initial NewOrderAndAuthzs call 1630 result, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{ 1631 AcctID: regA.Id, 1632 Identifiers: idents.ToProtoSlice(), 1633 }) 1634 // It should error since there is no result 1635 test.AssertError(t, err, "sa.GetOrderForNames did not return an error for an empty result") 1636 // The error should be a notfound error 1637 test.AssertErrorIs(t, err, berrors.NotFound) 1638 // The result should be nil because the initial order expired & we don't want 1639 // to return expired orders 1640 test.Assert(t, result == nil, "sa.GetOrderForNames returned non-nil result for expired order case") 1641 1642 // Create two valid authorizations 1643 authzExpires = fc.Now().Add(time.Hour) 1644 attemptedAt := fc.Now() 1645 authzIDC := createFinalizedAuthorization(t, sa, regA.Id, identifier.NewDNS("zombo.com"), authzExpires, "valid", attemptedAt) 1646 authzIDD := createFinalizedAuthorization(t, sa, regA.Id, identifier.NewDNS("welcome.to.zombo.com"), authzExpires, "valid", attemptedAt) 1647 1648 // Add a fresh order that uses the authorizations created above 1649 expires = fc.Now().Add(orderLifetime) 1650 order, err = sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{ 1651 NewOrder: &sapb.NewOrderRequest{ 1652 RegistrationID: regA.Id, 1653 Expires: timestamppb.New(expires), 1654 V2Authorizations: []int64{authzIDC, authzIDD}, 1655 Identifiers: idents.ToProtoSlice(), 1656 }, 1657 }) 1658 // It shouldn't error 1659 test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed") 1660 // The order ID shouldn't be nil 1661 test.AssertNotNil(t, order.Id, "NewOrderAndAuthzs returned with a nil Id") 1662 1663 // Call GetOrderForNames with the same account ID and set of names as 1664 // the earlier NewOrderAndAuthzs call 1665 result, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{ 1666 AcctID: regA.Id, 1667 Identifiers: idents.ToProtoSlice(), 1668 }) 1669 // It should not error since a ready order can be reused. 1670 test.AssertNotError(t, err, "sa.GetOrderForNames returned an unexpected error for ready order reuse") 1671 // The order returned should have the same ID as the order we created above 1672 test.AssertNotNil(t, result, "sa.GetOrderForNames returned nil result") 1673 test.AssertEquals(t, result.Id, order.Id) 1674 1675 // Set the order processing so it can be finalized 1676 _, err = sa.SetOrderProcessing(ctx, &sapb.OrderRequest{Id: order.Id}) 1677 test.AssertNotError(t, err, "sa.SetOrderProcessing failed") 1678 1679 // Finalize the order 1680 order.CertificateSerial = "cinnamon toast crunch" 1681 _, err = sa.FinalizeOrder(ctx, &sapb.FinalizeOrderRequest{Id: order.Id, CertificateSerial: order.CertificateSerial}) 1682 test.AssertNotError(t, err, "sa.FinalizeOrder failed") 1683 1684 // Call GetOrderForNames with the same account ID and set of names as 1685 // the earlier NewOrderAndAuthzs call 1686 result, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{ 1687 AcctID: regA.Id, 1688 Identifiers: idents.ToProtoSlice(), 1689 }) 1690 // It should error since a valid order should not be reused. 1691 test.AssertError(t, err, "sa.GetOrderForNames did not return an error for an empty result") 1692 // The error should be a notfound error 1693 test.AssertErrorIs(t, err, berrors.NotFound) 1694 // The result should be nil because the one matching order has been finalized 1695 // already 1696 test.Assert(t, result == nil, "sa.GetOrderForNames returned non-nil result for finalized order case") 1697 } 1698 1699 func TestStatusForOrder(t *testing.T) { 1700 sa, fc, cleanUp := initSA(t) 1701 defer cleanUp() 1702 1703 ctx := context.Background() 1704 expires := fc.Now().Add(time.Hour) 1705 alreadyExpired := expires.Add(-2 * time.Hour) 1706 attemptedAt := fc.Now() 1707 1708 // Create a registration to work with 1709 reg := createWorkingRegistration(t, sa) 1710 1711 // Create a pending authz, an expired authz, an invalid authz, a deactivated authz, 1712 // and a valid authz 1713 pendingID := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("pending.your.order.is.up"), expires) 1714 expiredID := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("expired.your.order.is.up"), alreadyExpired) 1715 invalidID := createFinalizedAuthorization(t, sa, reg.Id, identifier.NewDNS("invalid.your.order.is.up"), expires, "invalid", attemptedAt) 1716 validID := createFinalizedAuthorization(t, sa, reg.Id, identifier.NewDNS("valid.your.order.is.up"), expires, "valid", attemptedAt) 1717 deactivatedID := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("deactivated.your.order.is.up"), expires) 1718 _, err := sa.DeactivateAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: deactivatedID}) 1719 test.AssertNotError(t, err, "sa.DeactivateAuthorization2 failed") 1720 1721 testCases := []struct { 1722 Name string 1723 AuthorizationIDs []int64 1724 OrderIdents identifier.ACMEIdentifiers 1725 OrderExpires *timestamppb.Timestamp 1726 ExpectedStatus string 1727 SetProcessing bool 1728 Finalize bool 1729 }{ 1730 { 1731 Name: "Order with an invalid authz", 1732 OrderIdents: identifier.ACMEIdentifiers{ 1733 identifier.NewDNS("pending.your.order.is.up"), 1734 identifier.NewDNS("invalid.your.order.is.up"), 1735 identifier.NewDNS("deactivated.your.order.is.up"), 1736 identifier.NewDNS("valid.your.order.is.up"), 1737 }, 1738 AuthorizationIDs: []int64{pendingID, invalidID, deactivatedID, validID}, 1739 ExpectedStatus: string(core.StatusInvalid), 1740 }, 1741 { 1742 Name: "Order with an expired authz", 1743 OrderIdents: identifier.ACMEIdentifiers{ 1744 identifier.NewDNS("pending.your.order.is.up"), 1745 identifier.NewDNS("expired.your.order.is.up"), 1746 identifier.NewDNS("deactivated.your.order.is.up"), 1747 identifier.NewDNS("valid.your.order.is.up"), 1748 }, 1749 AuthorizationIDs: []int64{pendingID, expiredID, deactivatedID, validID}, 1750 ExpectedStatus: string(core.StatusInvalid), 1751 }, 1752 { 1753 Name: "Order with a deactivated authz", 1754 OrderIdents: identifier.ACMEIdentifiers{ 1755 identifier.NewDNS("pending.your.order.is.up"), 1756 identifier.NewDNS("deactivated.your.order.is.up"), 1757 identifier.NewDNS("valid.your.order.is.up"), 1758 }, 1759 AuthorizationIDs: []int64{pendingID, deactivatedID, validID}, 1760 ExpectedStatus: string(core.StatusInvalid), 1761 }, 1762 { 1763 Name: "Order with a pending authz", 1764 OrderIdents: identifier.ACMEIdentifiers{ 1765 identifier.NewDNS("valid.your.order.is.up"), 1766 identifier.NewDNS("pending.your.order.is.up"), 1767 }, 1768 AuthorizationIDs: []int64{validID, pendingID}, 1769 ExpectedStatus: string(core.StatusPending), 1770 }, 1771 { 1772 Name: "Order with only valid authzs, not yet processed or finalized", 1773 OrderIdents: identifier.ACMEIdentifiers{identifier.NewDNS("valid.your.order.is.up")}, 1774 AuthorizationIDs: []int64{validID}, 1775 ExpectedStatus: string(core.StatusReady), 1776 }, 1777 { 1778 Name: "Order with only valid authzs, set processing", 1779 OrderIdents: identifier.ACMEIdentifiers{identifier.NewDNS("valid.your.order.is.up")}, 1780 AuthorizationIDs: []int64{validID}, 1781 SetProcessing: true, 1782 ExpectedStatus: string(core.StatusProcessing), 1783 }, 1784 { 1785 Name: "Order with only valid authzs, not yet processed or finalized, OrderReadyStatus feature flag", 1786 OrderIdents: identifier.ACMEIdentifiers{identifier.NewDNS("valid.your.order.is.up")}, 1787 AuthorizationIDs: []int64{validID}, 1788 ExpectedStatus: string(core.StatusReady), 1789 }, 1790 { 1791 Name: "Order with only valid authzs, set processing", 1792 OrderIdents: identifier.ACMEIdentifiers{identifier.NewDNS("valid.your.order.is.up")}, 1793 AuthorizationIDs: []int64{validID}, 1794 SetProcessing: true, 1795 ExpectedStatus: string(core.StatusProcessing), 1796 }, 1797 { 1798 Name: "Order with only valid authzs, set processing and finalized", 1799 OrderIdents: identifier.ACMEIdentifiers{identifier.NewDNS("valid.your.order.is.up")}, 1800 AuthorizationIDs: []int64{validID}, 1801 SetProcessing: true, 1802 Finalize: true, 1803 ExpectedStatus: string(core.StatusValid), 1804 }, 1805 } 1806 1807 for _, tc := range testCases { 1808 t.Run(tc.Name, func(t *testing.T) { 1809 // If the testcase doesn't specify an order expiry use a default timestamp 1810 // in the near future. 1811 orderExpiry := tc.OrderExpires 1812 if !orderExpiry.IsValid() { 1813 orderExpiry = timestamppb.New(expires) 1814 } 1815 1816 newOrder, err := sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{ 1817 NewOrder: &sapb.NewOrderRequest{ 1818 RegistrationID: reg.Id, 1819 Expires: orderExpiry, 1820 V2Authorizations: tc.AuthorizationIDs, 1821 Identifiers: tc.OrderIdents.ToProtoSlice(), 1822 }, 1823 }) 1824 test.AssertNotError(t, err, "NewOrderAndAuthzs errored unexpectedly") 1825 // If requested, set the order to processing 1826 if tc.SetProcessing { 1827 _, err := sa.SetOrderProcessing(ctx, &sapb.OrderRequest{Id: newOrder.Id}) 1828 test.AssertNotError(t, err, "Error setting order to processing status") 1829 } 1830 // If requested, finalize the order 1831 if tc.Finalize { 1832 newOrder.CertificateSerial = "lucky charms" 1833 _, err = sa.FinalizeOrder(ctx, &sapb.FinalizeOrderRequest{Id: newOrder.Id, CertificateSerial: newOrder.CertificateSerial}) 1834 test.AssertNotError(t, err, "Error finalizing order") 1835 } 1836 // Fetch the order by ID to get its calculated status 1837 storedOrder, err := sa.GetOrder(ctx, &sapb.OrderRequest{Id: newOrder.Id}) 1838 test.AssertNotError(t, err, "GetOrder failed") 1839 // The status shouldn't be nil 1840 test.AssertNotNil(t, storedOrder.Status, "Order status was nil") 1841 // The status should match expected 1842 test.AssertEquals(t, storedOrder.Status, tc.ExpectedStatus) 1843 }) 1844 } 1845 1846 } 1847 1848 func TestUpdateChallengesDeleteUnused(t *testing.T) { 1849 sa, fc, cleanUp := initSA(t) 1850 defer cleanUp() 1851 1852 expires := fc.Now().Add(time.Hour) 1853 ctx := context.Background() 1854 attemptedAt := fc.Now() 1855 1856 // Create a valid authz 1857 reg := createWorkingRegistration(t, sa) 1858 authzID := createFinalizedAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expires, "valid", attemptedAt) 1859 1860 result, err := sa.GetAuthorization2(ctx, &sapb.AuthorizationID2{Id: authzID}) 1861 test.AssertNotError(t, err, "sa.GetAuthorization2 failed") 1862 1863 if len(result.Challenges) != 1 { 1864 t.Fatalf("expected 1 challenge left after finalization, got %d", len(result.Challenges)) 1865 } 1866 if result.Challenges[0].Status != string(core.StatusValid) { 1867 t.Errorf("expected challenge status %q, got %q", core.StatusValid, result.Challenges[0].Status) 1868 } 1869 if result.Challenges[0].Type != "http-01" { 1870 t.Errorf("expected challenge type %q, got %q", "http-01", result.Challenges[0].Type) 1871 } 1872 } 1873 1874 func TestRevokeCertificate(t *testing.T) { 1875 sa, fc, cleanUp := initSA(t) 1876 defer cleanUp() 1877 1878 reg := createWorkingRegistration(t, sa) 1879 // Add a cert to the DB to test with. 1880 serial, testCert := test.ThrowAwayCert(t, fc) 1881 issuedTime := sa.clk.Now() 1882 _, err := sa.AddSerial(ctx, &sapb.AddSerialRequest{ 1883 RegID: reg.Id, 1884 Serial: core.SerialToString(testCert.SerialNumber), 1885 Created: timestamppb.New(testCert.NotBefore), 1886 Expires: timestamppb.New(testCert.NotAfter), 1887 }) 1888 test.AssertNotError(t, err, "failed to add test serial") 1889 _, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ 1890 Der: testCert.Raw, 1891 RegID: reg.Id, 1892 Issued: timestamppb.New(issuedTime), 1893 IssuerNameID: 1, 1894 }) 1895 test.AssertNotError(t, err, "Couldn't add test cert") 1896 1897 status, err := sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial}) 1898 test.AssertNotError(t, err, "GetCertificateStatus failed") 1899 test.AssertEquals(t, core.OCSPStatus(status.Status), core.OCSPStatusGood) 1900 1901 fc.Add(1 * time.Hour) 1902 1903 now := fc.Now() 1904 reason := int64(1) 1905 1906 _, err = sa.RevokeCertificate(context.Background(), &sapb.RevokeCertificateRequest{ 1907 IssuerID: 1, 1908 Serial: serial, 1909 Date: timestamppb.New(now), 1910 Reason: reason, 1911 ShardIdx: 1, 1912 }) 1913 test.AssertNotError(t, err, "RevokeCertificate with no OCSP response should succeed") 1914 1915 status, err = sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial}) 1916 test.AssertNotError(t, err, "GetCertificateStatus failed") 1917 test.AssertEquals(t, core.OCSPStatus(status.Status), core.OCSPStatusRevoked) 1918 test.AssertEquals(t, status.RevokedReason, reason) 1919 test.AssertEquals(t, status.RevokedDate.AsTime(), now) 1920 test.AssertEquals(t, status.OcspLastUpdated.AsTime(), now) 1921 1922 _, err = sa.RevokeCertificate(context.Background(), &sapb.RevokeCertificateRequest{ 1923 IssuerID: 1, 1924 Serial: serial, 1925 Date: timestamppb.New(now), 1926 Reason: reason, 1927 }) 1928 test.AssertError(t, err, "RevokeCertificate should've failed when certificate already revoked") 1929 } 1930 1931 func TestRevokeCertificateWithShard(t *testing.T) { 1932 sa, fc, cleanUp := initSA(t) 1933 defer cleanUp() 1934 1935 // Add a cert to the DB to test with. 1936 reg := createWorkingRegistration(t, sa) 1937 eeCert, err := core.LoadCert("../test/hierarchy/ee-e1.cert.pem") 1938 test.AssertNotError(t, err, "failed to load test cert") 1939 _, err = sa.AddSerial(ctx, &sapb.AddSerialRequest{ 1940 RegID: reg.Id, 1941 Serial: core.SerialToString(eeCert.SerialNumber), 1942 Created: timestamppb.New(eeCert.NotBefore), 1943 Expires: timestamppb.New(eeCert.NotAfter), 1944 }) 1945 test.AssertNotError(t, err, "failed to add test serial") 1946 _, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ 1947 Der: eeCert.Raw, 1948 RegID: reg.Id, 1949 Issued: timestamppb.New(eeCert.NotBefore), 1950 IssuerNameID: 1, 1951 }) 1952 test.AssertNotError(t, err, "failed to add test cert") 1953 1954 serial := core.SerialToString(eeCert.SerialNumber) 1955 fc.Add(1 * time.Hour) 1956 now := fc.Now() 1957 reason := int64(1) 1958 1959 _, err = sa.RevokeCertificate(context.Background(), &sapb.RevokeCertificateRequest{ 1960 IssuerID: 1, 1961 ShardIdx: 9, 1962 Serial: serial, 1963 Date: timestamppb.New(now), 1964 Reason: reason, 1965 }) 1966 test.AssertNotError(t, err, "RevokeCertificate with no OCSP response should succeed") 1967 1968 status, err := sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial}) 1969 test.AssertNotError(t, err, "GetCertificateStatus failed") 1970 test.AssertEquals(t, core.OCSPStatus(status.Status), core.OCSPStatusRevoked) 1971 test.AssertEquals(t, status.RevokedReason, reason) 1972 test.AssertEquals(t, status.RevokedDate.AsTime(), now) 1973 test.AssertEquals(t, status.OcspLastUpdated.AsTime(), now) 1974 test.AssertEquals(t, status.NotAfter.AsTime(), eeCert.NotAfter) 1975 1976 var result revokedCertModel 1977 err = sa.dbMap.SelectOne( 1978 ctx, &result, `SELECT * FROM revokedCertificates WHERE serial = ?`, core.SerialToString(eeCert.SerialNumber)) 1979 test.AssertNotError(t, err, "should be exactly one row in revokedCertificates") 1980 test.AssertEquals(t, result.ShardIdx, int64(9)) 1981 test.AssertEquals(t, result.RevokedReason, revocation.KeyCompromise) 1982 } 1983 1984 func TestUpdateRevokedCertificate(t *testing.T) { 1985 sa, fc, cleanUp := initSA(t) 1986 defer cleanUp() 1987 1988 // Add a cert to the DB to test with. 1989 reg := createWorkingRegistration(t, sa) 1990 serial, testCert := test.ThrowAwayCert(t, fc) 1991 issuedTime := fc.Now() 1992 _, err := sa.AddSerial(ctx, &sapb.AddSerialRequest{ 1993 RegID: reg.Id, 1994 Serial: core.SerialToString(testCert.SerialNumber), 1995 Created: timestamppb.New(testCert.NotBefore), 1996 Expires: timestamppb.New(testCert.NotAfter), 1997 }) 1998 test.AssertNotError(t, err, "failed to add test serial") 1999 _, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ 2000 Der: testCert.Raw, 2001 RegID: reg.Id, 2002 Issued: timestamppb.New(issuedTime), 2003 IssuerNameID: 1, 2004 }) 2005 test.AssertNotError(t, err, "Couldn't add test cert") 2006 fc.Add(1 * time.Hour) 2007 2008 // Try to update it before its been revoked 2009 now := fc.Now() 2010 _, err = sa.UpdateRevokedCertificate(context.Background(), &sapb.RevokeCertificateRequest{ 2011 IssuerID: 1, 2012 Serial: serial, 2013 Date: timestamppb.New(now), 2014 Backdate: timestamppb.New(now), 2015 Reason: int64(revocation.KeyCompromise), 2016 Response: []byte{4, 5, 6}, 2017 ShardIdx: 1, 2018 }) 2019 test.AssertError(t, err, "UpdateRevokedCertificate should have failed") 2020 test.AssertContains(t, err.Error(), "no certificate with serial") 2021 2022 // Now revoke it, so we can update it. 2023 revokedTime := fc.Now() 2024 _, err = sa.RevokeCertificate(context.Background(), &sapb.RevokeCertificateRequest{ 2025 IssuerID: 1, 2026 Serial: serial, 2027 Date: timestamppb.New(revokedTime), 2028 Reason: int64(revocation.CessationOfOperation), 2029 Response: []byte{1, 2, 3}, 2030 ShardIdx: 1, 2031 }) 2032 test.AssertNotError(t, err, "RevokeCertificate failed") 2033 2034 // Double check that setup worked. 2035 status, err := sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial}) 2036 test.AssertNotError(t, err, "GetCertificateStatus failed") 2037 test.AssertEquals(t, core.OCSPStatus(status.Status), core.OCSPStatusRevoked) 2038 test.AssertEquals(t, revocation.Reason(status.RevokedReason), revocation.CessationOfOperation) 2039 fc.Add(1 * time.Hour) 2040 2041 // Try to update its revocation info with no backdate 2042 now = fc.Now() 2043 _, err = sa.UpdateRevokedCertificate(context.Background(), &sapb.RevokeCertificateRequest{ 2044 IssuerID: 1, 2045 Serial: serial, 2046 Date: timestamppb.New(now), 2047 Reason: int64(revocation.KeyCompromise), 2048 Response: []byte{4, 5, 6}, 2049 ShardIdx: 1, 2050 }) 2051 test.AssertError(t, err, "UpdateRevokedCertificate should have failed") 2052 test.AssertContains(t, err.Error(), "incomplete") 2053 2054 // Try to update its revocation info for a reason other than keyCompromise 2055 _, err = sa.UpdateRevokedCertificate(context.Background(), &sapb.RevokeCertificateRequest{ 2056 IssuerID: 1, 2057 Serial: serial, 2058 Date: timestamppb.New(now), 2059 Backdate: timestamppb.New(revokedTime), 2060 Reason: int64(revocation.Unspecified), 2061 Response: []byte{4, 5, 6}, 2062 ShardIdx: 1, 2063 }) 2064 test.AssertError(t, err, "UpdateRevokedCertificate should have failed") 2065 test.AssertContains(t, err.Error(), "cannot update revocation for any reason other than keyCompromise") 2066 2067 // Try to update the revocation info of the wrong certificate 2068 _, err = sa.UpdateRevokedCertificate(context.Background(), &sapb.RevokeCertificateRequest{ 2069 IssuerID: 1, 2070 Serial: "000000000000000000000000000000021bd5", 2071 Date: timestamppb.New(now), 2072 Backdate: timestamppb.New(revokedTime), 2073 Reason: int64(revocation.KeyCompromise), 2074 Response: []byte{4, 5, 6}, 2075 ShardIdx: 1, 2076 }) 2077 test.AssertError(t, err, "UpdateRevokedCertificate should have failed") 2078 test.AssertContains(t, err.Error(), "no certificate with serial") 2079 2080 // Try to update its revocation info with the wrong backdate 2081 _, err = sa.UpdateRevokedCertificate(context.Background(), &sapb.RevokeCertificateRequest{ 2082 IssuerID: 1, 2083 Serial: serial, 2084 Date: timestamppb.New(now), 2085 Backdate: timestamppb.New(now), 2086 Reason: int64(revocation.KeyCompromise), 2087 Response: []byte{4, 5, 6}, 2088 ShardIdx: 1, 2089 }) 2090 test.AssertError(t, err, "UpdateRevokedCertificate should have failed") 2091 test.AssertContains(t, err.Error(), "no certificate with serial") 2092 2093 // Try to update its revocation info with the wrong shard 2094 _, err = sa.UpdateRevokedCertificate(context.Background(), &sapb.RevokeCertificateRequest{ 2095 IssuerID: 1, 2096 Serial: serial, 2097 Date: timestamppb.New(now), 2098 Backdate: timestamppb.New(revokedTime), 2099 Reason: int64(revocation.KeyCompromise), 2100 Response: []byte{4, 5, 6}, 2101 ShardIdx: 2, 2102 }) 2103 test.AssertError(t, err, "UpdateRevokedCertificate should have failed") 2104 test.AssertContains(t, err.Error(), "mismatched shard index") 2105 2106 // Try to update its revocation info correctly 2107 _, err = sa.UpdateRevokedCertificate(context.Background(), &sapb.RevokeCertificateRequest{ 2108 IssuerID: 1, 2109 Serial: serial, 2110 Date: timestamppb.New(now), 2111 Backdate: timestamppb.New(revokedTime), 2112 Reason: int64(revocation.KeyCompromise), 2113 Response: []byte{4, 5, 6}, 2114 ShardIdx: 1, 2115 }) 2116 test.AssertNotError(t, err, "UpdateRevokedCertificate failed") 2117 } 2118 2119 func TestAddCertificateRenewalBit(t *testing.T) { 2120 sa, fc, cleanUp := initSA(t) 2121 defer cleanUp() 2122 2123 reg := createWorkingRegistration(t, sa) 2124 2125 assertIsRenewal := func(t *testing.T, issuedName string, expected bool) { 2126 t.Helper() 2127 var count int 2128 err := sa.dbMap.SelectOne( 2129 ctx, 2130 &count, 2131 `SELECT COUNT(*) FROM issuedNames 2132 WHERE reversedName = ? 2133 AND renewal = ?`, 2134 issuedName, 2135 expected, 2136 ) 2137 test.AssertNotError(t, err, "Unexpected error from SelectOne on issuedNames") 2138 test.AssertEquals(t, count, 1) 2139 } 2140 2141 // Add a certificate with never-before-seen identifiers. 2142 _, testCert := test.ThrowAwayCert(t, fc) 2143 _, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ 2144 Der: testCert.Raw, 2145 Issued: timestamppb.New(testCert.NotBefore), 2146 RegID: reg.Id, 2147 IssuerNameID: 1, 2148 }) 2149 test.AssertNotError(t, err, "Failed to add precertificate") 2150 _, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{ 2151 Der: testCert.Raw, 2152 RegID: reg.Id, 2153 Issued: timestamppb.New(testCert.NotBefore), 2154 }) 2155 test.AssertNotError(t, err, "Failed to add certificate") 2156 2157 // No identifier should have an issuedNames row marking it as a renewal. 2158 for _, name := range testCert.DNSNames { 2159 assertIsRenewal(t, reverseFQDN(name), false) 2160 } 2161 for _, ip := range testCert.IPAddresses { 2162 assertIsRenewal(t, ip.String(), false) 2163 } 2164 2165 // Make a new cert and add its FQDN set to the db so it will be considered a 2166 // renewal 2167 serial, testCert := test.ThrowAwayCert(t, fc) 2168 err = addFQDNSet(ctx, sa.dbMap, identifier.FromCert(testCert), serial, testCert.NotBefore, testCert.NotAfter) 2169 test.AssertNotError(t, err, "Failed to add identifier set") 2170 _, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ 2171 Der: testCert.Raw, 2172 Issued: timestamppb.New(testCert.NotBefore), 2173 RegID: reg.Id, 2174 IssuerNameID: 1, 2175 }) 2176 test.AssertNotError(t, err, "Failed to add precertificate") 2177 _, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{ 2178 Der: testCert.Raw, 2179 RegID: reg.Id, 2180 Issued: timestamppb.New(testCert.NotBefore), 2181 }) 2182 test.AssertNotError(t, err, "Failed to add certificate") 2183 2184 // Each identifier should have an issuedNames row marking it as a renewal. 2185 for _, name := range testCert.DNSNames { 2186 assertIsRenewal(t, reverseFQDN(name), true) 2187 } 2188 for _, ip := range testCert.IPAddresses { 2189 assertIsRenewal(t, ip.String(), true) 2190 } 2191 } 2192 2193 func TestFinalizeAuthorization2(t *testing.T) { 2194 sa, fc, cleanUp := initSA(t) 2195 defer cleanUp() 2196 2197 fc.Set(mustTime("2021-01-01 00:00")) 2198 2199 reg := createWorkingRegistration(t, sa) 2200 authzID := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("aaa"), fc.Now().Add(time.Hour)) 2201 expires := fc.Now().Add(time.Hour * 2).UTC() 2202 attemptedAt := fc.Now() 2203 ip, _ := netip.MustParseAddr("1.1.1.1").MarshalText() 2204 2205 _, err := sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{ 2206 Id: authzID, 2207 ValidationRecords: []*corepb.ValidationRecord{ 2208 { 2209 Hostname: "example.com", 2210 Port: "80", 2211 Url: "http://example.com", 2212 AddressUsed: ip, 2213 ResolverAddrs: []string{"resolver:5353"}, 2214 }, 2215 }, 2216 Status: string(core.StatusValid), 2217 Expires: timestamppb.New(expires), 2218 Attempted: string(core.ChallengeTypeHTTP01), 2219 AttemptedAt: timestamppb.New(attemptedAt), 2220 }) 2221 test.AssertNotError(t, err, "sa.FinalizeAuthorization2 failed") 2222 2223 dbVer, err := sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID}) 2224 test.AssertNotError(t, err, "sa.GetAuthorization2 failed") 2225 test.AssertEquals(t, dbVer.Status, string(core.StatusValid)) 2226 test.AssertEquals(t, dbVer.Expires.AsTime(), expires) 2227 test.AssertEquals(t, dbVer.Challenges[0].Status, string(core.StatusValid)) 2228 test.AssertEquals(t, len(dbVer.Challenges[0].Validationrecords), 1) 2229 test.AssertEquals(t, dbVer.Challenges[0].Validationrecords[0].Hostname, "example.com") 2230 test.AssertEquals(t, dbVer.Challenges[0].Validationrecords[0].Port, "80") 2231 test.AssertEquals(t, dbVer.Challenges[0].Validationrecords[0].ResolverAddrs[0], "resolver:5353") 2232 test.AssertEquals(t, dbVer.Challenges[0].Validated.AsTime(), attemptedAt) 2233 2234 authzID = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("aaa"), fc.Now().Add(time.Hour)) 2235 prob, _ := bgrpc.ProblemDetailsToPB(probs.Connection("it went bad captain")) 2236 2237 _, err = sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{ 2238 Id: authzID, 2239 ValidationRecords: []*corepb.ValidationRecord{ 2240 { 2241 Hostname: "example.com", 2242 Port: "80", 2243 Url: "http://example.com", 2244 AddressUsed: ip, 2245 ResolverAddrs: []string{"resolver:5353"}, 2246 }, 2247 }, 2248 ValidationError: prob, 2249 Status: string(core.StatusInvalid), 2250 Attempted: string(core.ChallengeTypeHTTP01), 2251 Expires: timestamppb.New(expires), 2252 }) 2253 test.AssertNotError(t, err, "sa.FinalizeAuthorization2 failed") 2254 2255 dbVer, err = sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID}) 2256 test.AssertNotError(t, err, "sa.GetAuthorization2 failed") 2257 test.AssertEquals(t, dbVer.Status, string(core.StatusInvalid)) 2258 test.AssertEquals(t, dbVer.Challenges[0].Status, string(core.StatusInvalid)) 2259 test.AssertEquals(t, len(dbVer.Challenges[0].Validationrecords), 1) 2260 test.AssertEquals(t, dbVer.Challenges[0].Validationrecords[0].Hostname, "example.com") 2261 test.AssertEquals(t, dbVer.Challenges[0].Validationrecords[0].Port, "80") 2262 test.AssertEquals(t, dbVer.Challenges[0].Validationrecords[0].ResolverAddrs[0], "resolver:5353") 2263 test.AssertDeepEquals(t, dbVer.Challenges[0].Error, prob) 2264 } 2265 2266 func TestRehydrateHostPort(t *testing.T) { 2267 sa, fc, cleanUp := initSA(t) 2268 defer cleanUp() 2269 2270 fc.Set(mustTime("2021-01-01 00:00")) 2271 2272 reg := createWorkingRegistration(t, sa) 2273 expires := fc.Now().Add(time.Hour * 2).UTC() 2274 attemptedAt := fc.Now() 2275 ip, _ := netip.MustParseAddr("1.1.1.1").MarshalText() 2276 2277 // Implicit good port with good scheme 2278 authzID := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("aaa"), fc.Now().Add(time.Hour)) 2279 _, err := sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{ 2280 Id: authzID, 2281 ValidationRecords: []*corepb.ValidationRecord{ 2282 { 2283 Hostname: "example.com", 2284 Port: "80", 2285 Url: "http://example.com", 2286 AddressUsed: ip, 2287 }, 2288 }, 2289 Status: string(core.StatusValid), 2290 Expires: timestamppb.New(expires), 2291 Attempted: string(core.ChallengeTypeHTTP01), 2292 AttemptedAt: timestamppb.New(attemptedAt), 2293 }) 2294 test.AssertNotError(t, err, "sa.FinalizeAuthorization2 failed") 2295 _, err = sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID}) 2296 test.AssertNotError(t, err, "rehydration failed in some fun and interesting way") 2297 2298 // Explicit good port with good scheme 2299 authzID = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("aaa"), fc.Now().Add(time.Hour)) 2300 _, err = sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{ 2301 Id: authzID, 2302 ValidationRecords: []*corepb.ValidationRecord{ 2303 { 2304 Hostname: "example.com", 2305 Port: "80", 2306 Url: "http://example.com:80", 2307 AddressUsed: ip, 2308 }, 2309 }, 2310 Status: string(core.StatusValid), 2311 Expires: timestamppb.New(expires), 2312 Attempted: string(core.ChallengeTypeHTTP01), 2313 AttemptedAt: timestamppb.New(attemptedAt), 2314 }) 2315 test.AssertNotError(t, err, "sa.FinalizeAuthorization2 failed") 2316 _, err = sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID}) 2317 test.AssertNotError(t, err, "rehydration failed in some fun and interesting way") 2318 2319 // Explicit bad port with good scheme 2320 authzID = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("aaa"), fc.Now().Add(time.Hour)) 2321 _, err = sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{ 2322 Id: authzID, 2323 ValidationRecords: []*corepb.ValidationRecord{ 2324 { 2325 Hostname: "example.com", 2326 Port: "444", 2327 Url: "http://example.com:444", 2328 AddressUsed: ip, 2329 }, 2330 }, 2331 Status: string(core.StatusValid), 2332 Expires: timestamppb.New(expires), 2333 Attempted: string(core.ChallengeTypeHTTP01), 2334 AttemptedAt: timestamppb.New(attemptedAt), 2335 }) 2336 test.AssertNotError(t, err, "sa.FinalizeAuthorization2 failed") 2337 _, err = sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID}) 2338 test.AssertError(t, err, "only ports 80/tcp and 443/tcp are allowed in URL \"http://example.com:444\"") 2339 2340 // Explicit bad port with bad scheme 2341 authzID = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("aaa"), fc.Now().Add(time.Hour)) 2342 _, err = sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{ 2343 Id: authzID, 2344 ValidationRecords: []*corepb.ValidationRecord{ 2345 { 2346 Hostname: "example.com", 2347 Port: "80", 2348 Url: "httpx://example.com", 2349 AddressUsed: ip, 2350 }, 2351 }, 2352 Status: string(core.StatusValid), 2353 Expires: timestamppb.New(expires), 2354 Attempted: string(core.ChallengeTypeHTTP01), 2355 AttemptedAt: timestamppb.New(attemptedAt), 2356 }) 2357 test.AssertNotError(t, err, "sa.FinalizeAuthorization2 failed") 2358 _, err = sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID}) 2359 test.AssertError(t, err, "unknown scheme \"httpx\" in URL \"httpx://example.com\"") 2360 2361 // Missing URL field 2362 authzID = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("aaa"), fc.Now().Add(time.Hour)) 2363 _, err = sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{ 2364 Id: authzID, 2365 ValidationRecords: []*corepb.ValidationRecord{ 2366 { 2367 Hostname: "example.com", 2368 Port: "80", 2369 AddressUsed: ip, 2370 }, 2371 }, 2372 Status: string(core.StatusValid), 2373 Expires: timestamppb.New(expires), 2374 Attempted: string(core.ChallengeTypeHTTP01), 2375 AttemptedAt: timestamppb.New(attemptedAt), 2376 }) 2377 test.AssertNotError(t, err, "sa.FinalizeAuthorization2 failed") 2378 _, err = sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID}) 2379 test.AssertError(t, err, "URL field cannot be empty") 2380 } 2381 2382 func TestCountPendingAuthorizations2(t *testing.T) { 2383 sa, fc, cleanUp := initSA(t) 2384 defer cleanUp() 2385 2386 reg := createWorkingRegistration(t, sa) 2387 expiresA := fc.Now().Add(time.Hour).UTC() 2388 expiresB := fc.Now().Add(time.Hour * 3).UTC() 2389 _ = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expiresA) 2390 _ = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expiresB) 2391 2392 // Registration has two new style pending authorizations 2393 regID := reg.Id 2394 count, err := sa.CountPendingAuthorizations2(context.Background(), &sapb.RegistrationID{ 2395 Id: regID, 2396 }) 2397 test.AssertNotError(t, err, "sa.CountPendingAuthorizations2 failed") 2398 test.AssertEquals(t, count.Count, int64(2)) 2399 2400 // Registration has two new style pending authorizations, one of which has expired 2401 fc.Add(time.Hour * 2) 2402 count, err = sa.CountPendingAuthorizations2(context.Background(), &sapb.RegistrationID{ 2403 Id: regID, 2404 }) 2405 test.AssertNotError(t, err, "sa.CountPendingAuthorizations2 failed") 2406 test.AssertEquals(t, count.Count, int64(1)) 2407 2408 // Registration with no authorizations should be 0 2409 noReg := reg.Id + 100 2410 count, err = sa.CountPendingAuthorizations2(context.Background(), &sapb.RegistrationID{ 2411 Id: noReg, 2412 }) 2413 test.AssertNotError(t, err, "sa.CountPendingAuthorizations2 failed") 2414 test.AssertEquals(t, count.Count, int64(0)) 2415 } 2416 2417 func TestAuthzModelMapToPB(t *testing.T) { 2418 baseExpires := time.Now() 2419 input := map[identifier.ACMEIdentifier]authzModel{ 2420 identifier.NewDNS("example.com"): { 2421 ID: 123, 2422 IdentifierType: 0, 2423 IdentifierValue: "example.com", 2424 RegistrationID: 77, 2425 Status: 1, 2426 Expires: baseExpires, 2427 Challenges: 4, 2428 }, 2429 identifier.NewDNS("www.example.com"): { 2430 ID: 124, 2431 IdentifierType: 0, 2432 IdentifierValue: "www.example.com", 2433 RegistrationID: 77, 2434 Status: 1, 2435 Expires: baseExpires, 2436 Challenges: 1, 2437 }, 2438 identifier.NewDNS("other.example.net"): { 2439 ID: 125, 2440 IdentifierType: 0, 2441 IdentifierValue: "other.example.net", 2442 RegistrationID: 77, 2443 Status: 1, 2444 Expires: baseExpires, 2445 Challenges: 3, 2446 }, 2447 identifier.NewIP(netip.MustParseAddr("10.10.10.10")): { 2448 ID: 126, 2449 IdentifierType: 1, 2450 IdentifierValue: "10.10.10.10", 2451 RegistrationID: 77, 2452 Status: 1, 2453 Expires: baseExpires, 2454 Challenges: 5, 2455 }, 2456 } 2457 2458 out, err := authzModelMapToPB(input) 2459 if err != nil { 2460 t.Fatal(err) 2461 } 2462 2463 for _, authzPB := range out.Authzs { 2464 model, ok := input[identifier.FromProto(authzPB.Identifier)] 2465 if !ok { 2466 t.Errorf("output had element for %q, an identifier not present in input", authzPB.Identifier.Value) 2467 } 2468 test.AssertEquals(t, authzPB.Id, fmt.Sprintf("%d", model.ID)) 2469 test.AssertEquals(t, authzPB.Identifier.Type, string(uintToIdentifierType[model.IdentifierType])) 2470 test.AssertEquals(t, authzPB.Identifier.Value, model.IdentifierValue) 2471 test.AssertEquals(t, authzPB.RegistrationID, model.RegistrationID) 2472 test.AssertEquals(t, authzPB.Status, string(uintToStatus[model.Status])) 2473 gotTime := authzPB.Expires.AsTime() 2474 if !model.Expires.Equal(gotTime) { 2475 t.Errorf("Times didn't match. Got %s, expected %s (%s)", gotTime, model.Expires, authzPB.Expires.AsTime()) 2476 } 2477 if len(authzPB.Challenges) != bits.OnesCount(uint(model.Challenges)) { 2478 t.Errorf("wrong number of challenges for %q: got %d, expected %d", authzPB.Identifier.Value, 2479 len(authzPB.Challenges), bits.OnesCount(uint(model.Challenges))) 2480 } 2481 switch model.Challenges { 2482 case 1: 2483 test.AssertEquals(t, authzPB.Challenges[0].Type, "http-01") 2484 case 3: 2485 test.AssertEquals(t, authzPB.Challenges[0].Type, "http-01") 2486 test.AssertEquals(t, authzPB.Challenges[1].Type, "dns-01") 2487 case 4: 2488 test.AssertEquals(t, authzPB.Challenges[0].Type, "tls-alpn-01") 2489 } 2490 2491 delete(input, identifier.FromProto(authzPB.Identifier)) 2492 } 2493 2494 for k := range input { 2495 t.Errorf("hostname %q was not present in output", k) 2496 } 2497 } 2498 2499 func TestGetValidOrderAuthorizations2(t *testing.T) { 2500 for _, val := range []bool{true, false} { 2501 features.Set(features.Config{ 2502 StoreAuthzsInOrders: val, 2503 }) 2504 if features.Get().StoreAuthzsInOrders && os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" { 2505 t.Skip("need DB migration to test StoreAuthzsInOrders=true") 2506 } 2507 sa, fc, cleanup := initSA(t) 2508 defer cleanup() 2509 2510 // Create three new valid authorizations 2511 reg := createWorkingRegistration(t, sa) 2512 identA := identifier.NewDNS("a.example.com") 2513 identB := identifier.NewDNS("b.example.com") 2514 identC := identifier.NewIP(netip.MustParseAddr("3fff:aaa:aaaa:aaaa:abad:0ff1:cec0:ffee")) 2515 expires := fc.Now().Add(time.Hour * 24 * 7).UTC() 2516 attemptedAt := fc.Now() 2517 2518 authzIDA := createFinalizedAuthorization(t, sa, reg.Id, identA, expires, "valid", attemptedAt) 2519 authzIDB := createFinalizedAuthorization(t, sa, reg.Id, identB, expires, "valid", attemptedAt) 2520 authzIDC := createFinalizedAuthorization(t, sa, reg.Id, identC, expires, "valid", attemptedAt) 2521 2522 orderExpr := fc.Now().Truncate(time.Second) 2523 order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{ 2524 NewOrder: &sapb.NewOrderRequest{ 2525 RegistrationID: reg.Id, 2526 Expires: timestamppb.New(orderExpr), 2527 Identifiers: []*corepb.Identifier{ 2528 identifier.NewDNS("a.example.com").ToProto(), 2529 identifier.NewDNS("b.example.com").ToProto(), 2530 identifier.NewIP(netip.MustParseAddr("3fff:aaa:aaaa:aaaa:abad:0ff1:cec0:ffee")).ToProto(), 2531 }, 2532 V2Authorizations: []int64{authzIDA, authzIDB, authzIDC}, 2533 }, 2534 }) 2535 test.AssertNotError(t, err, "AddOrder failed") 2536 2537 authzPBs, err := sa.GetValidOrderAuthorizations2( 2538 context.Background(), 2539 &sapb.GetValidOrderAuthorizationsRequest{ 2540 Id: order.Id, 2541 AcctID: reg.Id, 2542 }) 2543 test.AssertNotError(t, err, "sa.GetValidOrderAuthorizations failed") 2544 test.AssertNotNil(t, authzPBs, "sa.GetValidOrderAuthorizations result was nil") 2545 test.AssertEquals(t, len(authzPBs.Authzs), 3) 2546 2547 identsToCheck := map[identifier.ACMEIdentifier]int64{ 2548 identifier.NewDNS("a.example.com"): authzIDA, 2549 identifier.NewDNS("b.example.com"): authzIDB, 2550 identifier.NewIP(netip.MustParseAddr("3fff:aaa:aaaa:aaaa:abad:0ff1:cec0:ffee")): authzIDC, 2551 } 2552 for _, a := range authzPBs.Authzs { 2553 ident := identifier.ACMEIdentifier{Type: identifier.IdentifierType(a.Identifier.Type), Value: a.Identifier.Value} 2554 if fmt.Sprintf("%d", identsToCheck[ident]) != a.Id { 2555 t.Fatalf("incorrect identifier %q with id %s", a.Identifier.Value, a.Id) 2556 } 2557 test.AssertEquals(t, a.Expires.AsTime(), expires) 2558 delete(identsToCheck, ident) 2559 } 2560 2561 // Getting the order authorizations for an order that doesn't exist should return nothing 2562 missingID := int64(0xC0FFEEEEEEE) 2563 authzPBs, err = sa.GetValidOrderAuthorizations2( 2564 context.Background(), 2565 &sapb.GetValidOrderAuthorizationsRequest{ 2566 Id: missingID, 2567 AcctID: reg.Id, 2568 }) 2569 test.AssertNotError(t, err, "sa.GetValidOrderAuthorizations failed") 2570 test.AssertEquals(t, len(authzPBs.Authzs), 0) 2571 } 2572 } 2573 2574 func TestCountInvalidAuthorizations2(t *testing.T) { 2575 sa, fc, cleanUp := initSA(t) 2576 defer cleanUp() 2577 2578 fc.Add(time.Hour) 2579 reg := createWorkingRegistration(t, sa) 2580 idents := identifier.ACMEIdentifiers{ 2581 identifier.NewDNS("aaa"), 2582 identifier.NewIP(netip.MustParseAddr("10.10.10.10")), 2583 } 2584 for _, ident := range idents { 2585 // Create two authorizations, one pending, one invalid 2586 expiresA := fc.Now().Add(time.Hour).UTC() 2587 expiresB := fc.Now().Add(time.Hour * 3).UTC() 2588 attemptedAt := fc.Now() 2589 _ = createFinalizedAuthorization(t, sa, reg.Id, ident, expiresA, "invalid", attemptedAt) 2590 _ = createPendingAuthorization(t, sa, reg.Id, ident, expiresB) 2591 2592 earliest := fc.Now().Add(-time.Hour).UTC() 2593 latest := fc.Now().Add(time.Hour * 5).UTC() 2594 count, err := sa.CountInvalidAuthorizations2(context.Background(), &sapb.CountInvalidAuthorizationsRequest{ 2595 RegistrationID: reg.Id, 2596 Identifier: ident.ToProto(), 2597 Range: &sapb.Range{ 2598 Earliest: timestamppb.New(earliest), 2599 Latest: timestamppb.New(latest), 2600 }, 2601 }) 2602 test.AssertNotError(t, err, "sa.CountInvalidAuthorizations2 failed") 2603 test.AssertEquals(t, count.Count, int64(1)) 2604 } 2605 } 2606 2607 func TestGetValidAuthorizations2(t *testing.T) { 2608 sa, fc, cleanUp := initSA(t) 2609 defer cleanUp() 2610 2611 var aaa int64 2612 { 2613 tokenStr := core.NewToken() 2614 token, err := base64.RawURLEncoding.DecodeString(tokenStr) 2615 test.AssertNotError(t, err, "computing test authorization challenge token") 2616 2617 profile := "test" 2618 attempted := challTypeToUint[string(core.ChallengeTypeHTTP01)] 2619 attemptedAt := fc.Now() 2620 vr, _ := json.Marshal([]core.ValidationRecord{}) 2621 2622 am := authzModel{ 2623 IdentifierType: identifierTypeToUint[string(identifier.TypeDNS)], 2624 IdentifierValue: "aaa", 2625 RegistrationID: 1, 2626 CertificateProfileName: &profile, 2627 Status: statusToUint[core.StatusValid], 2628 Expires: fc.Now().Add(24 * time.Hour), 2629 Challenges: 1 << challTypeToUint[string(core.ChallengeTypeHTTP01)], 2630 Attempted: &attempted, 2631 AttemptedAt: &attemptedAt, 2632 Token: token, 2633 ValidationError: nil, 2634 ValidationRecord: vr, 2635 } 2636 2637 err = sa.dbMap.Insert(context.Background(), &am) 2638 test.AssertNotError(t, err, "failed to insert valid authz") 2639 2640 aaa = am.ID 2641 } 2642 2643 var dac int64 2644 { 2645 tokenStr := core.NewToken() 2646 token, err := base64.RawURLEncoding.DecodeString(tokenStr) 2647 test.AssertNotError(t, err, "computing test authorization challenge token") 2648 2649 profile := "test" 2650 attempted := challTypeToUint[string(core.ChallengeTypeDNSAccount01)] 2651 attemptedAt := fc.Now() 2652 vr, _ := json.Marshal([]core.ValidationRecord{}) 2653 2654 am := authzModel{ 2655 IdentifierType: identifierTypeToUint[string(identifier.TypeDNS)], 2656 IdentifierValue: "aaa", 2657 RegistrationID: 3, 2658 CertificateProfileName: &profile, 2659 Status: statusToUint[core.StatusValid], 2660 Expires: fc.Now().Add(24 * time.Hour), 2661 Challenges: 1 << challTypeToUint[string(core.ChallengeTypeDNSAccount01)], 2662 Attempted: &attempted, 2663 AttemptedAt: &attemptedAt, 2664 Token: token, 2665 ValidationError: nil, 2666 ValidationRecord: vr, 2667 } 2668 err = sa.dbMap.Insert(context.Background(), &am) 2669 test.AssertNotError(t, err, "failed to insert valid authz with dns-account-01") 2670 dac = am.ID 2671 } 2672 2673 for _, tc := range []struct { 2674 name string 2675 regID int64 2676 identifiers []*corepb.Identifier 2677 profile string 2678 validUntil time.Time 2679 wantIDs []int64 2680 }{ 2681 { 2682 name: "happy path, DNS identifier", 2683 regID: 1, 2684 identifiers: []*corepb.Identifier{identifier.NewDNS("aaa").ToProto()}, 2685 profile: "test", 2686 validUntil: fc.Now().Add(time.Hour), 2687 wantIDs: []int64{aaa}, 2688 }, 2689 { 2690 name: "happy path, dns-account-01 challenge", 2691 regID: 3, 2692 identifiers: []*corepb.Identifier{identifier.NewDNS("aaa").ToProto()}, 2693 profile: "test", 2694 validUntil: fc.Now().Add(time.Hour), 2695 wantIDs: []int64{dac}, 2696 }, 2697 { 2698 name: "different identifier type", 2699 regID: 1, 2700 identifiers: []*corepb.Identifier{identifier.NewIP(netip.MustParseAddr("10.10.10.10")).ToProto()}, 2701 profile: "test", 2702 validUntil: fc.Now().Add(time.Hour), 2703 wantIDs: []int64{}, 2704 }, 2705 { 2706 name: "different regID", 2707 regID: 2, 2708 identifiers: []*corepb.Identifier{identifier.NewDNS("aaa").ToProto()}, 2709 profile: "test", 2710 validUntil: fc.Now().Add(time.Hour), 2711 wantIDs: []int64{}, 2712 }, 2713 { 2714 name: "different DNS identifier", 2715 regID: 1, 2716 identifiers: []*corepb.Identifier{identifier.NewDNS("bbb").ToProto()}, 2717 profile: "test", 2718 validUntil: fc.Now().Add(time.Hour), 2719 wantIDs: []int64{}, 2720 }, 2721 { 2722 name: "different profile", 2723 regID: 1, 2724 identifiers: []*corepb.Identifier{identifier.NewDNS("aaa").ToProto()}, 2725 profile: "other", 2726 validUntil: fc.Now().Add(time.Hour), 2727 wantIDs: []int64{}, 2728 }, 2729 { 2730 name: "too-far-out validUntil", 2731 regID: 2, 2732 identifiers: []*corepb.Identifier{identifier.NewDNS("aaa").ToProto()}, 2733 profile: "test", 2734 validUntil: fc.Now().Add(25 * time.Hour), 2735 wantIDs: []int64{}, 2736 }, 2737 } { 2738 t.Run(tc.name, func(t *testing.T) { 2739 got, err := sa.GetValidAuthorizations2(context.Background(), &sapb.GetValidAuthorizationsRequest{ 2740 RegistrationID: tc.regID, 2741 Identifiers: tc.identifiers, 2742 Profile: tc.profile, 2743 ValidUntil: timestamppb.New(tc.validUntil), 2744 }) 2745 if err != nil { 2746 t.Fatalf("GetValidAuthorizations2 got error %q, want success", err) 2747 } 2748 2749 var gotIDs []int64 2750 for _, authz := range got.Authzs { 2751 id, err := strconv.Atoi(authz.Id) 2752 if err != nil { 2753 t.Fatalf("parsing authz id: %s", err) 2754 } 2755 gotIDs = append(gotIDs, int64(id)) 2756 } 2757 2758 slices.Sort(gotIDs) 2759 slices.Sort(tc.wantIDs) 2760 if !slices.Equal(gotIDs, tc.wantIDs) { 2761 t.Errorf("GetValidAuthorizations2() = %+v, want %+v", gotIDs, tc.wantIDs) 2762 } 2763 }) 2764 } 2765 } 2766 2767 func TestGetOrderExpired(t *testing.T) { 2768 for _, storeAuthzsInOrders := range []bool{true, false} { 2769 if storeAuthzsInOrders && os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" { 2770 t.Skip("need DB migration to test StoreAuthzsInOrders=true") 2771 } 2772 features.Set(features.Config{ 2773 StoreAuthzsInOrders: storeAuthzsInOrders, 2774 }) 2775 sa, fc, cleanUp := initSA(t) 2776 defer cleanUp() 2777 fc.Add(time.Hour * 5) 2778 now := fc.Now() 2779 reg := createWorkingRegistration(t, sa) 2780 order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{ 2781 NewOrder: &sapb.NewOrderRequest{ 2782 RegistrationID: reg.Id, 2783 Expires: timestamppb.New(now.Add(-time.Hour)), 2784 Identifiers: []*corepb.Identifier{identifier.NewDNS("example.com").ToProto()}, 2785 V2Authorizations: []int64{666}, 2786 }, 2787 }) 2788 test.AssertNotError(t, err, "NewOrderAndAuthzs failed") 2789 _, err = sa.GetOrder(context.Background(), &sapb.OrderRequest{ 2790 Id: order.Id, 2791 }) 2792 test.AssertError(t, err, "GetOrder didn't fail for an expired order") 2793 test.AssertErrorIs(t, err, berrors.NotFound) 2794 } 2795 } 2796 2797 func TestBlockedKey(t *testing.T) { 2798 sa, _, cleanUp := initSA(t) 2799 defer cleanUp() 2800 2801 hashA := make([]byte, 32) 2802 hashA[0] = 1 2803 hashB := make([]byte, 32) 2804 hashB[0] = 2 2805 2806 added := time.Now() 2807 source := "API" 2808 _, err := sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{ 2809 KeyHash: hashA, 2810 Added: timestamppb.New(added), 2811 Source: source, 2812 }) 2813 test.AssertNotError(t, err, "AddBlockedKey failed") 2814 _, err = sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{ 2815 KeyHash: hashA, 2816 Added: timestamppb.New(added), 2817 Source: source, 2818 }) 2819 test.AssertNotError(t, err, "AddBlockedKey failed with duplicate insert") 2820 2821 comment := "testing comments" 2822 _, err = sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{ 2823 KeyHash: hashB, 2824 Added: timestamppb.New(added), 2825 Source: source, 2826 Comment: comment, 2827 }) 2828 test.AssertNotError(t, err, "AddBlockedKey failed") 2829 2830 exists, err := sa.KeyBlocked(context.Background(), &sapb.SPKIHash{ 2831 KeyHash: hashA, 2832 }) 2833 test.AssertNotError(t, err, "KeyBlocked failed") 2834 test.Assert(t, exists != nil, "*sapb.Exists is nil") 2835 test.Assert(t, exists.Exists, "KeyBlocked returned false for blocked key") 2836 exists, err = sa.KeyBlocked(context.Background(), &sapb.SPKIHash{ 2837 KeyHash: hashB, 2838 }) 2839 test.AssertNotError(t, err, "KeyBlocked failed") 2840 test.Assert(t, exists != nil, "*sapb.Exists is nil") 2841 test.Assert(t, exists.Exists, "KeyBlocked returned false for blocked key") 2842 exists, err = sa.KeyBlocked(context.Background(), &sapb.SPKIHash{ 2843 KeyHash: []byte{5}, 2844 }) 2845 test.AssertNotError(t, err, "KeyBlocked failed") 2846 test.Assert(t, exists != nil, "*sapb.Exists is nil") 2847 test.Assert(t, !exists.Exists, "KeyBlocked returned true for non-blocked key") 2848 } 2849 2850 func TestAddBlockedKeyUnknownSource(t *testing.T) { 2851 sa, fc, cleanUp := initSA(t) 2852 defer cleanUp() 2853 2854 _, err := sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{ 2855 KeyHash: []byte{1, 2, 3}, 2856 Added: timestamppb.New(fc.Now()), 2857 Source: "heyo", 2858 }) 2859 test.AssertError(t, err, "AddBlockedKey didn't fail with unknown source") 2860 test.AssertEquals(t, err.Error(), "unknown source") 2861 } 2862 2863 func TestBlockedKeyRevokedBy(t *testing.T) { 2864 sa, fc, cleanUp := initSA(t) 2865 defer cleanUp() 2866 2867 now := fc.Now() 2868 _, err := sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{ 2869 KeyHash: []byte{1}, 2870 Added: timestamppb.New(now), 2871 Source: "API", 2872 }) 2873 test.AssertNotError(t, err, "AddBlockedKey failed") 2874 2875 _, err = sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{ 2876 KeyHash: []byte{2}, 2877 Added: timestamppb.New(now), 2878 Source: "API", 2879 RevokedBy: 1, 2880 }) 2881 test.AssertNotError(t, err, "AddBlockedKey failed") 2882 } 2883 2884 func TestIncidentsForSerial(t *testing.T) { 2885 sa, _, cleanUp := initSA(t) 2886 defer cleanUp() 2887 2888 testSADbMap, err := DBMapForTest(vars.DBConnSAFullPerms) 2889 test.AssertNotError(t, err, "Couldn't create test dbMap") 2890 2891 testIncidentsDbMap, err := DBMapForTest(vars.DBConnIncidentsFullPerms) 2892 test.AssertNotError(t, err, "Couldn't create test dbMap") 2893 defer test.ResetIncidentsTestDatabase(t) 2894 2895 weekAgo := sa.clk.Now().Add(-time.Hour * 24 * 7) 2896 2897 // Add a disabled incident. 2898 err = testSADbMap.Insert(ctx, &incidentModel{ 2899 SerialTable: "incident_foo", 2900 URL: "https://example.com/foo-incident", 2901 RenewBy: sa.clk.Now().Add(time.Hour * 24 * 7), 2902 Enabled: false, 2903 }) 2904 test.AssertNotError(t, err, "Failed to insert disabled incident") 2905 2906 // No incidents are enabled, so this should return in error. 2907 result, err := sa.IncidentsForSerial(context.Background(), &sapb.Serial{Serial: "1337"}) 2908 test.AssertNotError(t, err, "fetching from no incidents") 2909 test.AssertEquals(t, len(result.Incidents), 0) 2910 2911 // Add an enabled incident. 2912 err = testSADbMap.Insert(ctx, &incidentModel{ 2913 SerialTable: "incident_bar", 2914 URL: "https://example.com/test-incident", 2915 RenewBy: sa.clk.Now().Add(time.Hour * 24 * 7), 2916 Enabled: true, 2917 }) 2918 test.AssertNotError(t, err, "Failed to insert enabled incident") 2919 2920 // Add a row to the incident table with serial '1338'. 2921 one := int64(1) 2922 affectedCertA := incidentSerialModel{ 2923 Serial: "1338", 2924 RegistrationID: &one, 2925 OrderID: &one, 2926 LastNoticeSent: &weekAgo, 2927 } 2928 _, err = testIncidentsDbMap.ExecContext(ctx, 2929 fmt.Sprintf("INSERT INTO incident_bar (%s) VALUES ('%s', %d, %d, '%s')", 2930 "serial, registrationID, orderID, lastNoticeSent", 2931 affectedCertA.Serial, 2932 affectedCertA.RegistrationID, 2933 affectedCertA.OrderID, 2934 affectedCertA.LastNoticeSent.Format(time.DateTime), 2935 ), 2936 ) 2937 test.AssertNotError(t, err, "Error while inserting row for '1338' into incident table") 2938 2939 // The incident table should not contain a row with serial '1337'. 2940 result, err = sa.IncidentsForSerial(context.Background(), &sapb.Serial{Serial: "1337"}) 2941 test.AssertNotError(t, err, "fetching from one incident") 2942 test.AssertEquals(t, len(result.Incidents), 0) 2943 2944 // Add a row to the incident table with serial '1337'. 2945 two := int64(2) 2946 affectedCertB := incidentSerialModel{ 2947 Serial: "1337", 2948 RegistrationID: &two, 2949 OrderID: &two, 2950 LastNoticeSent: &weekAgo, 2951 } 2952 _, err = testIncidentsDbMap.ExecContext(ctx, 2953 fmt.Sprintf("INSERT INTO incident_bar (%s) VALUES ('%s', %d, %d, '%s')", 2954 "serial, registrationID, orderID, lastNoticeSent", 2955 affectedCertB.Serial, 2956 affectedCertB.RegistrationID, 2957 affectedCertB.OrderID, 2958 affectedCertB.LastNoticeSent.Format(time.DateTime), 2959 ), 2960 ) 2961 test.AssertNotError(t, err, "Error while inserting row for '1337' into incident table") 2962 2963 // The incident table should now contain a row with serial '1337'. 2964 result, err = sa.IncidentsForSerial(context.Background(), &sapb.Serial{Serial: "1337"}) 2965 test.AssertNotError(t, err, "Failed to retrieve incidents for serial") 2966 test.AssertEquals(t, len(result.Incidents), 1) 2967 } 2968 2969 func TestSerialsForIncident(t *testing.T) { 2970 sa, _, cleanUp := initSA(t) 2971 defer cleanUp() 2972 2973 testIncidentsDbMap, err := DBMapForTest(vars.DBConnIncidentsFullPerms) 2974 test.AssertNotError(t, err, "Couldn't create test dbMap") 2975 defer test.ResetIncidentsTestDatabase(t) 2976 2977 // Request serials from a malformed incident table name. 2978 mockServerStream := &fakeServerStream[sapb.IncidentSerial]{} 2979 err = sa.SerialsForIncident( 2980 &sapb.SerialsForIncidentRequest{ 2981 IncidentTable: "incidesnt_Baz", 2982 }, 2983 mockServerStream, 2984 ) 2985 test.AssertError(t, err, "Expected error for malformed table name") 2986 test.AssertContains(t, err.Error(), "malformed table name \"incidesnt_Baz\"") 2987 2988 // Request serials from another malformed incident table name. 2989 mockServerStream = &fakeServerStream[sapb.IncidentSerial]{} 2990 longTableName := "incident_l" + strings.Repeat("o", 1000) + "ng" 2991 err = sa.SerialsForIncident( 2992 &sapb.SerialsForIncidentRequest{ 2993 IncidentTable: longTableName, 2994 }, 2995 mockServerStream, 2996 ) 2997 test.AssertError(t, err, "Expected error for long table name") 2998 test.AssertContains(t, err.Error(), fmt.Sprintf("malformed table name %q", longTableName)) 2999 3000 // Request serials for an incident table which doesn't exists. 3001 mockServerStream = &fakeServerStream[sapb.IncidentSerial]{} 3002 err = sa.SerialsForIncident( 3003 &sapb.SerialsForIncidentRequest{ 3004 IncidentTable: "incident_baz", 3005 }, 3006 mockServerStream, 3007 ) 3008 test.AssertError(t, err, "Expected error for nonexistent table name") 3009 3010 // Assert that the error is a MySQL error so we can inspect the error code. 3011 var mysqlErr *mysql.MySQLError 3012 if errors.As(err, &mysqlErr) { 3013 // We expect the error code to be 1146 (ER_NO_SUCH_TABLE): 3014 // https://mariadb.com/kb/en/mariadb-error-codes/ 3015 test.AssertEquals(t, mysqlErr.Number, uint16(1146)) 3016 } else { 3017 t.Fatalf("Expected MySQL Error 1146 (ER_NO_SUCH_TABLE) from Recv(), got %q", err) 3018 } 3019 3020 // Request serials from table 'incident_foo', which we expect to exist but 3021 // be empty. 3022 stream := make(chan *sapb.IncidentSerial) 3023 mockServerStream = &fakeServerStream[sapb.IncidentSerial]{output: stream} 3024 go func() { 3025 err = sa.SerialsForIncident( 3026 &sapb.SerialsForIncidentRequest{ 3027 IncidentTable: "incident_foo", 3028 }, 3029 mockServerStream, 3030 ) 3031 close(stream) // Let our main test thread continue. 3032 }() 3033 for range stream { 3034 t.Fatal("No serials should have been written to this stream") 3035 } 3036 test.AssertNotError(t, err, "Error calling SerialsForIncident on empty table") 3037 3038 // Add 4 rows of incident serials to 'incident_foo'. 3039 expectedSerials := map[string]bool{ 3040 "1335": true, "1336": true, "1337": true, "1338": true, 3041 } 3042 for i := range expectedSerials { 3043 randInt := func() int64 { return mrand.Int64() } 3044 _, err := testIncidentsDbMap.ExecContext(ctx, 3045 fmt.Sprintf("INSERT INTO incident_foo (%s) VALUES ('%s', %d, %d, '%s')", 3046 "serial, registrationID, orderID, lastNoticeSent", 3047 i, 3048 randInt(), 3049 randInt(), 3050 sa.clk.Now().Add(time.Hour*24*7).Format(time.DateTime), 3051 ), 3052 ) 3053 test.AssertNotError(t, err, fmt.Sprintf("Error while inserting row for '%s' into incident table", i)) 3054 } 3055 3056 // Request all 4 serials from the incident table we just added entries to. 3057 stream = make(chan *sapb.IncidentSerial) 3058 mockServerStream = &fakeServerStream[sapb.IncidentSerial]{output: stream} 3059 go func() { 3060 err = sa.SerialsForIncident( 3061 &sapb.SerialsForIncidentRequest{ 3062 IncidentTable: "incident_foo", 3063 }, 3064 mockServerStream, 3065 ) 3066 close(stream) 3067 }() 3068 receivedSerials := make(map[string]bool) 3069 for serial := range stream { 3070 if len(receivedSerials) > 4 { 3071 t.Fatal("Received too many serials") 3072 } 3073 if _, ok := receivedSerials[serial.Serial]; ok { 3074 t.Fatalf("Received serial %q more than once", serial.Serial) 3075 } 3076 receivedSerials[serial.Serial] = true 3077 } 3078 test.AssertDeepEquals(t, receivedSerials, map[string]bool{ 3079 "1335": true, "1336": true, "1337": true, "1338": true, 3080 }) 3081 test.AssertNotError(t, err, "Error getting serials for incident") 3082 } 3083 3084 func TestGetRevokedCertsByShard(t *testing.T) { 3085 sa, _, cleanUp := initSA(t) 3086 defer cleanUp() 3087 3088 // Add a cert to the DB to test with. We use AddPrecertificate because it sets 3089 // up the certificateStatus row we need. This particular cert has a notAfter 3090 // date of Mar 6 2023, and we lie about its IssuerNameID to make things easy. 3091 reg := createWorkingRegistration(t, sa) 3092 eeCert, err := core.LoadCert("../test/hierarchy/ee-e1.cert.pem") 3093 test.AssertNotError(t, err, "failed to load test cert") 3094 _, err = sa.AddSerial(ctx, &sapb.AddSerialRequest{ 3095 RegID: reg.Id, 3096 Serial: core.SerialToString(eeCert.SerialNumber), 3097 Created: timestamppb.New(eeCert.NotBefore), 3098 Expires: timestamppb.New(eeCert.NotAfter), 3099 }) 3100 test.AssertNotError(t, err, "failed to add test serial") 3101 _, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ 3102 Der: eeCert.Raw, 3103 RegID: reg.Id, 3104 Issued: timestamppb.New(eeCert.NotBefore), 3105 IssuerNameID: 1, 3106 }) 3107 test.AssertNotError(t, err, "failed to add test cert") 3108 3109 // Check that it worked. 3110 status, err := sa.GetCertificateStatus( 3111 ctx, &sapb.Serial{Serial: core.SerialToString(eeCert.SerialNumber)}) 3112 test.AssertNotError(t, err, "GetCertificateStatus failed") 3113 test.AssertEquals(t, core.OCSPStatus(status.Status), core.OCSPStatusGood) 3114 3115 // Here's a little helper func we'll use to call GetRevokedCertsByShard and count 3116 // how many results it returned. 3117 countRevokedCerts := func(req *sapb.GetRevokedCertsByShardRequest) (int, error) { 3118 stream := make(chan *corepb.CRLEntry) 3119 mockServerStream := &fakeServerStream[corepb.CRLEntry]{output: stream} 3120 var err error 3121 go func() { 3122 err = sa.GetRevokedCertsByShard(req, mockServerStream) 3123 close(stream) 3124 }() 3125 entriesReceived := 0 3126 for range stream { 3127 entriesReceived++ 3128 } 3129 return entriesReceived, err 3130 } 3131 3132 // The basic request covers a time range and shard that should include this certificate. 3133 basicRequest := &sapb.GetRevokedCertsByShardRequest{ 3134 IssuerNameID: 1, 3135 ShardIdx: 9, 3136 ExpiresAfter: mustTimestamp("2023-03-01 00:00"), 3137 RevokedBefore: mustTimestamp("2023-04-01 00:00"), 3138 } 3139 3140 // Nothing's been revoked yet. Count should be zero. 3141 count, err := countRevokedCerts(basicRequest) 3142 test.AssertNotError(t, err, "zero rows shouldn't result in error") 3143 test.AssertEquals(t, count, 0) 3144 3145 // Revoke the certificate, providing the ShardIdx so it gets written into 3146 // both the certificateStatus and revokedCertificates tables. 3147 _, err = sa.RevokeCertificate(context.Background(), &sapb.RevokeCertificateRequest{ 3148 IssuerID: 1, 3149 Serial: core.SerialToString(eeCert.SerialNumber), 3150 Date: mustTimestamp("2023-01-01 00:00"), 3151 Reason: 1, 3152 Response: []byte{1, 2, 3}, 3153 ShardIdx: 9, 3154 }) 3155 test.AssertNotError(t, err, "failed to revoke test cert") 3156 3157 // Check that it worked in the most basic way. 3158 c, err := sa.dbMap.SelectNullInt( 3159 ctx, "SELECT count(*) FROM revokedCertificates") 3160 test.AssertNotError(t, err, "SELECT from revokedCertificates failed") 3161 test.Assert(t, c.Valid, "SELECT from revokedCertificates got no result") 3162 test.AssertEquals(t, c.Int64, int64(1)) 3163 3164 // Asking for revoked certs now should return one result. 3165 count, err = countRevokedCerts(basicRequest) 3166 test.AssertNotError(t, err, "normal usage shouldn't result in error") 3167 test.AssertEquals(t, count, 1) 3168 3169 // Asking for revoked certs from a different issuer should return zero results. 3170 count, err = countRevokedCerts(&sapb.GetRevokedCertsByShardRequest{ 3171 IssuerNameID: 5678, 3172 ShardIdx: basicRequest.ShardIdx, 3173 ExpiresAfter: basicRequest.ExpiresAfter, 3174 RevokedBefore: basicRequest.RevokedBefore, 3175 }) 3176 test.AssertNotError(t, err, "zero rows shouldn't result in error") 3177 test.AssertEquals(t, count, 0) 3178 3179 // Asking for revoked certs from a different shard should return zero results. 3180 count, err = countRevokedCerts(&sapb.GetRevokedCertsByShardRequest{ 3181 IssuerNameID: basicRequest.IssuerNameID, 3182 ShardIdx: 8, 3183 ExpiresAfter: basicRequest.ExpiresAfter, 3184 RevokedBefore: basicRequest.RevokedBefore, 3185 }) 3186 test.AssertNotError(t, err, "zero rows shouldn't result in error") 3187 test.AssertEquals(t, count, 0) 3188 3189 // Asking for revoked certs with an old RevokedBefore should return no results. 3190 count, err = countRevokedCerts(&sapb.GetRevokedCertsByShardRequest{ 3191 IssuerNameID: basicRequest.IssuerNameID, 3192 ShardIdx: basicRequest.ShardIdx, 3193 ExpiresAfter: basicRequest.ExpiresAfter, 3194 RevokedBefore: mustTimestamp("2020-03-01 00:00"), 3195 }) 3196 test.AssertNotError(t, err, "zero rows shouldn't result in error") 3197 test.AssertEquals(t, count, 0) 3198 } 3199 3200 func TestLeaseOldestCRLShard(t *testing.T) { 3201 sa, clk, cleanUp := initSA(t) 3202 defer cleanUp() 3203 3204 // Create 8 shards: 4 for each of 2 issuers. For each issuer, one shard is 3205 // currently leased, three are available, and one of those failed to update. 3206 _, err := sa.dbMap.ExecContext(ctx, 3207 `INSERT INTO crlShards (issuerID, idx, thisUpdate, nextUpdate, leasedUntil) VALUES 3208 (1, 0, ?, ?, ?), 3209 (1, 1, ?, ?, ?), 3210 (1, 2, ?, ?, ?), 3211 (1, 3, NULL, NULL, ?), 3212 (2, 0, ?, ?, ?), 3213 (2, 1, ?, ?, ?), 3214 (2, 2, ?, ?, ?), 3215 (2, 3, NULL, NULL, ?);`, 3216 clk.Now().Add(-7*24*time.Hour), clk.Now().Add(3*24*time.Hour), clk.Now().Add(time.Hour), 3217 clk.Now().Add(-6*24*time.Hour), clk.Now().Add(4*24*time.Hour), clk.Now().Add(-6*24*time.Hour), 3218 clk.Now().Add(-5*24*time.Hour), clk.Now().Add(5*24*time.Hour), clk.Now().Add(-5*24*time.Hour), 3219 clk.Now().Add(-4*24*time.Hour), 3220 clk.Now().Add(-7*24*time.Hour), clk.Now().Add(3*24*time.Hour), clk.Now().Add(time.Hour), 3221 clk.Now().Add(-6*24*time.Hour), clk.Now().Add(4*24*time.Hour), clk.Now().Add(-6*24*time.Hour), 3222 clk.Now().Add(-5*24*time.Hour), clk.Now().Add(5*24*time.Hour), clk.Now().Add(-5*24*time.Hour), 3223 clk.Now().Add(-4*24*time.Hour), 3224 ) 3225 test.AssertNotError(t, err, "setting up test shards") 3226 3227 until := clk.Now().Add(time.Hour).Truncate(time.Second).UTC() 3228 var untilModel struct { 3229 LeasedUntil time.Time `db:"leasedUntil"` 3230 } 3231 3232 // Leasing from a fully-leased subset should fail. 3233 _, err = sa.leaseOldestCRLShard( 3234 context.Background(), 3235 &sapb.LeaseCRLShardRequest{ 3236 IssuerNameID: 1, 3237 MinShardIdx: 0, 3238 MaxShardIdx: 0, 3239 Until: timestamppb.New(until), 3240 }, 3241 ) 3242 test.AssertError(t, err, "leasing when all shards are leased") 3243 3244 // Leasing any known shard should return the never-before-leased one (3). 3245 res, err := sa.leaseOldestCRLShard( 3246 context.Background(), 3247 &sapb.LeaseCRLShardRequest{ 3248 IssuerNameID: 1, 3249 MinShardIdx: 0, 3250 MaxShardIdx: 3, 3251 Until: timestamppb.New(until), 3252 }, 3253 ) 3254 test.AssertNotError(t, err, "leasing available shard") 3255 test.AssertEquals(t, res.IssuerNameID, int64(1)) 3256 test.AssertEquals(t, res.ShardIdx, int64(3)) 3257 3258 err = sa.dbMap.SelectOne( 3259 ctx, 3260 &untilModel, 3261 `SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`, 3262 res.IssuerNameID, 3263 res.ShardIdx, 3264 ) 3265 test.AssertNotError(t, err, "getting updated lease timestamp") 3266 test.Assert(t, untilModel.LeasedUntil.Equal(until), "checking updated lease timestamp") 3267 3268 // Leasing any known shard *again* should now return the oldest one (1). 3269 res, err = sa.leaseOldestCRLShard( 3270 context.Background(), 3271 &sapb.LeaseCRLShardRequest{ 3272 IssuerNameID: 1, 3273 MinShardIdx: 0, 3274 MaxShardIdx: 3, 3275 Until: timestamppb.New(until), 3276 }, 3277 ) 3278 test.AssertNotError(t, err, "leasing available shard") 3279 test.AssertEquals(t, res.IssuerNameID, int64(1)) 3280 test.AssertEquals(t, res.ShardIdx, int64(1)) 3281 3282 err = sa.dbMap.SelectOne( 3283 ctx, 3284 &untilModel, 3285 `SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`, 3286 res.IssuerNameID, 3287 res.ShardIdx, 3288 ) 3289 test.AssertNotError(t, err, "getting updated lease timestamp") 3290 test.Assert(t, untilModel.LeasedUntil.Equal(until), "checking updated lease timestamp") 3291 3292 // Leasing from a superset of known shards should succeed and return one of 3293 // the previously-unknown shards. 3294 res, err = sa.leaseOldestCRLShard( 3295 context.Background(), 3296 &sapb.LeaseCRLShardRequest{ 3297 IssuerNameID: 2, 3298 MinShardIdx: 0, 3299 MaxShardIdx: 7, 3300 Until: timestamppb.New(until), 3301 }, 3302 ) 3303 test.AssertNotError(t, err, "leasing available shard") 3304 test.AssertEquals(t, res.IssuerNameID, int64(2)) 3305 test.Assert(t, res.ShardIdx >= 4, "checking leased index") 3306 test.Assert(t, res.ShardIdx <= 7, "checking leased index") 3307 3308 err = sa.dbMap.SelectOne( 3309 ctx, 3310 &untilModel, 3311 `SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`, 3312 res.IssuerNameID, 3313 res.ShardIdx, 3314 ) 3315 test.AssertNotError(t, err, "getting updated lease timestamp") 3316 test.Assert(t, untilModel.LeasedUntil.Equal(until), "checking updated lease timestamp") 3317 } 3318 3319 func TestLeaseSpecificCRLShard(t *testing.T) { 3320 sa, clk, cleanUp := initSA(t) 3321 defer cleanUp() 3322 3323 // Create 8 shards: 4 for each of 2 issuers. For each issuer, one shard is 3324 // currently leased, three are available, and one of those failed to update. 3325 _, err := sa.dbMap.ExecContext(ctx, 3326 `INSERT INTO crlShards (issuerID, idx, thisUpdate, nextUpdate, leasedUntil) VALUES 3327 (1, 0, ?, ?, ?), 3328 (1, 1, ?, ?, ?), 3329 (1, 2, ?, ?, ?), 3330 (1, 3, NULL, NULL, ?), 3331 (2, 0, ?, ?, ?), 3332 (2, 1, ?, ?, ?), 3333 (2, 2, ?, ?, ?), 3334 (2, 3, NULL, NULL, ?);`, 3335 clk.Now().Add(-7*24*time.Hour), clk.Now().Add(3*24*time.Hour), clk.Now().Add(time.Hour), 3336 clk.Now().Add(-6*24*time.Hour), clk.Now().Add(4*24*time.Hour), clk.Now().Add(-6*24*time.Hour), 3337 clk.Now().Add(-5*24*time.Hour), clk.Now().Add(5*24*time.Hour), clk.Now().Add(-5*24*time.Hour), 3338 clk.Now().Add(-4*24*time.Hour), 3339 clk.Now().Add(-7*24*time.Hour), clk.Now().Add(3*24*time.Hour), clk.Now().Add(time.Hour), 3340 clk.Now().Add(-6*24*time.Hour), clk.Now().Add(4*24*time.Hour), clk.Now().Add(-6*24*time.Hour), 3341 clk.Now().Add(-5*24*time.Hour), clk.Now().Add(5*24*time.Hour), clk.Now().Add(-5*24*time.Hour), 3342 clk.Now().Add(-4*24*time.Hour), 3343 ) 3344 test.AssertNotError(t, err, "setting up test shards") 3345 3346 until := clk.Now().Add(time.Hour).Truncate(time.Second).UTC() 3347 var untilModel struct { 3348 LeasedUntil time.Time `db:"leasedUntil"` 3349 } 3350 3351 // Leasing an unleased shard should work. 3352 res, err := sa.leaseSpecificCRLShard( 3353 context.Background(), 3354 &sapb.LeaseCRLShardRequest{ 3355 IssuerNameID: 1, 3356 MinShardIdx: 1, 3357 MaxShardIdx: 1, 3358 Until: timestamppb.New(until), 3359 }, 3360 ) 3361 test.AssertNotError(t, err, "leasing available shard") 3362 test.AssertEquals(t, res.IssuerNameID, int64(1)) 3363 test.AssertEquals(t, res.ShardIdx, int64(1)) 3364 3365 err = sa.dbMap.SelectOne( 3366 ctx, 3367 &untilModel, 3368 `SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`, 3369 res.IssuerNameID, 3370 res.ShardIdx, 3371 ) 3372 test.AssertNotError(t, err, "getting updated lease timestamp") 3373 test.Assert(t, untilModel.LeasedUntil.Equal(until), "checking updated lease timestamp") 3374 3375 // Leasing a never-before-leased shard should work. 3376 res, err = sa.leaseSpecificCRLShard( 3377 context.Background(), 3378 &sapb.LeaseCRLShardRequest{ 3379 IssuerNameID: 2, 3380 MinShardIdx: 3, 3381 MaxShardIdx: 3, 3382 Until: timestamppb.New(until), 3383 }, 3384 ) 3385 test.AssertNotError(t, err, "leasing available shard") 3386 test.AssertEquals(t, res.IssuerNameID, int64(2)) 3387 test.AssertEquals(t, res.ShardIdx, int64(3)) 3388 3389 err = sa.dbMap.SelectOne( 3390 ctx, 3391 &untilModel, 3392 `SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`, 3393 res.IssuerNameID, 3394 res.ShardIdx, 3395 ) 3396 test.AssertNotError(t, err, "getting updated lease timestamp") 3397 test.Assert(t, untilModel.LeasedUntil.Equal(until), "checking updated lease timestamp") 3398 3399 // Leasing a previously-unknown specific shard should work (to ease the 3400 // transition into using leasing). 3401 res, err = sa.leaseSpecificCRLShard( 3402 context.Background(), 3403 &sapb.LeaseCRLShardRequest{ 3404 IssuerNameID: 1, 3405 MinShardIdx: 9, 3406 MaxShardIdx: 9, 3407 Until: timestamppb.New(until), 3408 }, 3409 ) 3410 test.AssertNotError(t, err, "leasing unknown shard") 3411 3412 err = sa.dbMap.SelectOne( 3413 ctx, 3414 &untilModel, 3415 `SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`, 3416 res.IssuerNameID, 3417 res.ShardIdx, 3418 ) 3419 test.AssertNotError(t, err, "getting updated lease timestamp") 3420 test.Assert(t, untilModel.LeasedUntil.Equal(until), "checking updated lease timestamp") 3421 3422 // Leasing a leased shard should fail. 3423 _, err = sa.leaseSpecificCRLShard( 3424 context.Background(), 3425 &sapb.LeaseCRLShardRequest{ 3426 IssuerNameID: 1, 3427 MinShardIdx: 0, 3428 MaxShardIdx: 0, 3429 Until: timestamppb.New(until), 3430 }, 3431 ) 3432 test.AssertError(t, err, "leasing unavailable shard") 3433 3434 // Leasing more than one shard should fail. 3435 _, err = sa.leaseSpecificCRLShard( 3436 context.Background(), 3437 &sapb.LeaseCRLShardRequest{ 3438 IssuerNameID: 1, 3439 MinShardIdx: 1, 3440 MaxShardIdx: 2, 3441 Until: timestamppb.New(until), 3442 }, 3443 ) 3444 test.AssertError(t, err, "did not lease one specific shard") 3445 } 3446 3447 func TestUpdateCRLShard(t *testing.T) { 3448 sa, clk, cleanUp := initSA(t) 3449 defer cleanUp() 3450 3451 // Create 8 shards: 4 for each of 2 issuers. For each issuer, one shard is 3452 // currently leased, three are available, and one of those failed to update. 3453 _, err := sa.dbMap.ExecContext(ctx, 3454 `INSERT INTO crlShards (issuerID, idx, thisUpdate, nextUpdate, leasedUntil) VALUES 3455 (1, 0, ?, ?, ?), 3456 (1, 1, ?, ?, ?), 3457 (1, 2, ?, ?, ?), 3458 (1, 3, NULL, NULL, ?), 3459 (2, 0, ?, ?, ?), 3460 (2, 1, ?, ?, ?), 3461 (2, 2, ?, ?, ?), 3462 (2, 3, NULL, NULL, ?);`, 3463 clk.Now().Add(-7*24*time.Hour), clk.Now().Add(3*24*time.Hour), clk.Now().Add(time.Hour), 3464 clk.Now().Add(-6*24*time.Hour), clk.Now().Add(4*24*time.Hour), clk.Now().Add(-6*24*time.Hour), 3465 clk.Now().Add(-5*24*time.Hour), clk.Now().Add(5*24*time.Hour), clk.Now().Add(-5*24*time.Hour), 3466 clk.Now().Add(-4*24*time.Hour), 3467 clk.Now().Add(-7*24*time.Hour), clk.Now().Add(3*24*time.Hour), clk.Now().Add(time.Hour), 3468 clk.Now().Add(-6*24*time.Hour), clk.Now().Add(4*24*time.Hour), clk.Now().Add(-6*24*time.Hour), 3469 clk.Now().Add(-5*24*time.Hour), clk.Now().Add(5*24*time.Hour), clk.Now().Add(-5*24*time.Hour), 3470 clk.Now().Add(-4*24*time.Hour), 3471 ) 3472 test.AssertNotError(t, err, "setting up test shards") 3473 3474 thisUpdate := clk.Now().Truncate(time.Second).UTC() 3475 var crlModel struct { 3476 ThisUpdate *time.Time 3477 NextUpdate *time.Time 3478 } 3479 3480 // Updating a leased shard should work. 3481 _, err = sa.UpdateCRLShard( 3482 context.Background(), 3483 &sapb.UpdateCRLShardRequest{ 3484 IssuerNameID: 1, 3485 ShardIdx: 0, 3486 ThisUpdate: timestamppb.New(thisUpdate), 3487 NextUpdate: timestamppb.New(thisUpdate.Add(10 * 24 * time.Hour)), 3488 }, 3489 ) 3490 test.AssertNotError(t, err, "updating leased shard") 3491 3492 err = sa.dbMap.SelectOne( 3493 ctx, 3494 &crlModel, 3495 `SELECT thisUpdate FROM crlShards WHERE issuerID = 1 AND idx = 0 LIMIT 1`, 3496 ) 3497 test.AssertNotError(t, err, "getting updated thisUpdate timestamp") 3498 test.Assert(t, crlModel.ThisUpdate.Equal(thisUpdate), "checking updated thisUpdate timestamp") 3499 3500 // Updating an unleased shard should work. 3501 _, err = sa.UpdateCRLShard( 3502 context.Background(), 3503 &sapb.UpdateCRLShardRequest{ 3504 IssuerNameID: 1, 3505 ShardIdx: 1, 3506 ThisUpdate: timestamppb.New(thisUpdate), 3507 NextUpdate: timestamppb.New(thisUpdate.Add(10 * 24 * time.Hour)), 3508 }, 3509 ) 3510 test.AssertNotError(t, err, "updating unleased shard") 3511 3512 err = sa.dbMap.SelectOne( 3513 ctx, 3514 &crlModel, 3515 `SELECT thisUpdate FROM crlShards WHERE issuerID = 1 AND idx = 1 LIMIT 1`, 3516 ) 3517 test.AssertNotError(t, err, "getting updated thisUpdate timestamp") 3518 test.Assert(t, crlModel.ThisUpdate.Equal(thisUpdate), "checking updated thisUpdate timestamp") 3519 3520 // Updating without supplying a NextUpdate should work. 3521 _, err = sa.UpdateCRLShard( 3522 context.Background(), 3523 &sapb.UpdateCRLShardRequest{ 3524 IssuerNameID: 1, 3525 ShardIdx: 3, 3526 ThisUpdate: timestamppb.New(thisUpdate.Add(time.Second)), 3527 }, 3528 ) 3529 test.AssertNotError(t, err, "updating shard without NextUpdate") 3530 3531 err = sa.dbMap.SelectOne( 3532 ctx, 3533 &crlModel, 3534 `SELECT nextUpdate FROM crlShards WHERE issuerID = 1 AND idx = 3 LIMIT 1`, 3535 ) 3536 test.AssertNotError(t, err, "getting updated nextUpdate timestamp") 3537 test.AssertBoxedNil(t, crlModel.NextUpdate, "checking updated nextUpdate timestamp") 3538 3539 // Updating a shard to an earlier time should fail. 3540 _, err = sa.UpdateCRLShard( 3541 context.Background(), 3542 &sapb.UpdateCRLShardRequest{ 3543 IssuerNameID: 1, 3544 ShardIdx: 1, 3545 ThisUpdate: timestamppb.New(thisUpdate.Add(-24 * time.Hour)), 3546 NextUpdate: timestamppb.New(thisUpdate.Add(9 * 24 * time.Hour)), 3547 }, 3548 ) 3549 test.AssertError(t, err, "updating shard to an earlier time") 3550 3551 // Updating an unknown shard should fail. 3552 _, err = sa.UpdateCRLShard( 3553 context.Background(), 3554 &sapb.UpdateCRLShardRequest{ 3555 IssuerNameID: 1, 3556 ShardIdx: 4, 3557 ThisUpdate: timestamppb.New(thisUpdate), 3558 NextUpdate: timestamppb.New(thisUpdate.Add(10 * 24 * time.Hour)), 3559 }, 3560 ) 3561 test.AssertError(t, err, "updating an unknown shard") 3562 } 3563 3564 func TestReplacementOrderExists(t *testing.T) { 3565 sa, fc, cleanUp := initSA(t) 3566 defer cleanUp() 3567 3568 oldCertSerial := "1234567890" 3569 3570 // Check that a non-existent replacement order does not exist. 3571 exists, err := sa.ReplacementOrderExists(ctx, &sapb.Serial{Serial: oldCertSerial}) 3572 test.AssertNotError(t, err, "failed to check for replacement order") 3573 test.Assert(t, !exists.Exists, "replacement for non-existent serial should not exist") 3574 3575 // Create a test registration to reference. 3576 reg := createWorkingRegistration(t, sa) 3577 3578 // Add one valid authz. 3579 expires := fc.Now().Add(time.Hour) 3580 attemptedAt := fc.Now() 3581 authzID := createFinalizedAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expires, "valid", attemptedAt) 3582 3583 // Add a new order in pending status with no certificate serial. 3584 expires1Year := sa.clk.Now().Add(365 * 24 * time.Hour) 3585 order, err := sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{ 3586 NewOrder: &sapb.NewOrderRequest{ 3587 RegistrationID: reg.Id, 3588 Expires: timestamppb.New(expires1Year), 3589 Identifiers: []*corepb.Identifier{identifier.NewDNS("example.com").ToProto()}, 3590 V2Authorizations: []int64{authzID}, 3591 }, 3592 }) 3593 test.AssertNotError(t, err, "NewOrderAndAuthzs failed") 3594 3595 // Set the order to processing so it can be finalized 3596 _, err = sa.SetOrderProcessing(ctx, &sapb.OrderRequest{Id: order.Id}) 3597 test.AssertNotError(t, err, "SetOrderProcessing failed") 3598 3599 // Finalize the order with a certificate oldCertSerial. 3600 order.CertificateSerial = oldCertSerial 3601 _, err = sa.FinalizeOrder(ctx, &sapb.FinalizeOrderRequest{Id: order.Id, CertificateSerial: order.CertificateSerial}) 3602 test.AssertNotError(t, err, "FinalizeOrder failed") 3603 3604 // Create a replacement order. 3605 order, err = sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{ 3606 NewOrder: &sapb.NewOrderRequest{ 3607 RegistrationID: reg.Id, 3608 Expires: timestamppb.New(expires1Year), 3609 Identifiers: []*corepb.Identifier{identifier.NewDNS("example.com").ToProto()}, 3610 V2Authorizations: []int64{authzID}, 3611 ReplacesSerial: oldCertSerial, 3612 }, 3613 }) 3614 test.AssertNotError(t, err, "NewOrderAndAuthzs failed") 3615 3616 // Check that a pending replacement order exists. 3617 exists, err = sa.ReplacementOrderExists(ctx, &sapb.Serial{Serial: oldCertSerial}) 3618 test.AssertNotError(t, err, "failed to check for replacement order") 3619 test.Assert(t, exists.Exists, "replacement order should exist") 3620 3621 // Set the order to processing so it can be finalized. 3622 _, err = sa.SetOrderProcessing(ctx, &sapb.OrderRequest{Id: order.Id}) 3623 test.AssertNotError(t, err, "SetOrderProcessing failed") 3624 3625 // Check that a replacement order in processing still exists. 3626 exists, err = sa.ReplacementOrderExists(ctx, &sapb.Serial{Serial: oldCertSerial}) 3627 test.AssertNotError(t, err, "failed to check for replacement order") 3628 test.Assert(t, exists.Exists, "replacement order in processing should still exist") 3629 3630 order.CertificateSerial = "0123456789" 3631 _, err = sa.FinalizeOrder(ctx, &sapb.FinalizeOrderRequest{Id: order.Id, CertificateSerial: order.CertificateSerial}) 3632 test.AssertNotError(t, err, "FinalizeOrder failed") 3633 3634 // Check that a finalized replacement order still exists. 3635 exists, err = sa.ReplacementOrderExists(ctx, &sapb.Serial{Serial: oldCertSerial}) 3636 test.AssertNotError(t, err, "failed to check for replacement order") 3637 test.Assert(t, exists.Exists, "replacement order in processing should still exist") 3638 3639 // Try updating the replacement order. 3640 3641 // Create a replacement order. 3642 newReplacementOrder, err := sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{ 3643 NewOrder: &sapb.NewOrderRequest{ 3644 RegistrationID: reg.Id, 3645 Expires: timestamppb.New(expires1Year), 3646 Identifiers: []*corepb.Identifier{identifier.NewDNS("example.com").ToProto()}, 3647 V2Authorizations: []int64{authzID}, 3648 ReplacesSerial: oldCertSerial, 3649 }, 3650 }) 3651 test.AssertNotError(t, err, "NewOrderAndAuthzs failed") 3652 3653 // Fetch the replacement order so we can ensure it was updated. 3654 var replacementRow replacementOrderModel 3655 err = sa.dbReadOnlyMap.SelectOne( 3656 ctx, 3657 &replacementRow, 3658 "SELECT * FROM replacementOrders WHERE serial = ? LIMIT 1", 3659 oldCertSerial, 3660 ) 3661 test.AssertNotError(t, err, "SELECT from replacementOrders failed") 3662 test.AssertEquals(t, newReplacementOrder.Id, replacementRow.OrderID) 3663 test.AssertEquals(t, newReplacementOrder.Expires.AsTime(), replacementRow.OrderExpires) 3664 } 3665 3666 func TestGetSerialsByKey(t *testing.T) { 3667 sa, fc, cleanUp := initSA(t) 3668 defer cleanUp() 3669 3670 // Insert four rows into keyHashToSerial: two that should match the query, 3671 // one that should not match due to keyHash mismatch, and one that should not 3672 // match due to being already expired. 3673 expectedHash := make([]byte, 32) 3674 expectedHash[0] = 1 3675 differentHash := make([]byte, 32) 3676 differentHash[0] = 2 3677 inserts := []keyHashModel{ 3678 { 3679 KeyHash: expectedHash, 3680 CertSerial: "1", 3681 CertNotAfter: fc.Now().Add(time.Hour), 3682 }, 3683 { 3684 KeyHash: expectedHash, 3685 CertSerial: "2", 3686 CertNotAfter: fc.Now().Add(2 * time.Hour), 3687 }, 3688 { 3689 KeyHash: expectedHash, 3690 CertSerial: "3", 3691 CertNotAfter: fc.Now().Add(-1 * time.Hour), 3692 }, 3693 { 3694 KeyHash: differentHash, 3695 CertSerial: "4", 3696 CertNotAfter: fc.Now().Add(time.Hour), 3697 }, 3698 } 3699 3700 for _, row := range inserts { 3701 err := sa.dbMap.Insert(context.Background(), &row) 3702 test.AssertNotError(t, err, "inserting test keyHash") 3703 } 3704 3705 // Expect the result res to have two entries. 3706 res := make(chan *sapb.Serial) 3707 stream := &fakeServerStream[sapb.Serial]{output: res} 3708 var err error 3709 go func() { 3710 err = sa.GetSerialsByKey(&sapb.SPKIHash{KeyHash: expectedHash}, stream) 3711 close(res) // Let our main test thread continue. 3712 }() 3713 3714 var seen []string 3715 for serial := range res { 3716 if !slices.Contains([]string{"1", "2"}, serial.Serial) { 3717 t.Errorf("Received unexpected serial %q", serial.Serial) 3718 } 3719 if slices.Contains(seen, serial.Serial) { 3720 t.Errorf("Received serial %q more than once", serial.Serial) 3721 } 3722 seen = append(seen, serial.Serial) 3723 } 3724 test.AssertNotError(t, err, "calling GetSerialsByKey") 3725 test.AssertEquals(t, len(seen), 2) 3726 } 3727 3728 func TestGetSerialsByAccount(t *testing.T) { 3729 sa, fc, cleanUp := initSA(t) 3730 defer cleanUp() 3731 3732 expectedReg := createWorkingRegistration(t, sa) 3733 3734 // Insert three rows into the serials table: two that should match the query, 3735 // and one that should not match due to being already expired. We do not here 3736 // test filtering on the regID itself, because our test setup makes it very 3737 // hard to insert two fake registrations rows with different IDs. 3738 inserts := []recordedSerialModel{ 3739 { 3740 Serial: "1", 3741 RegistrationID: expectedReg.Id, 3742 Created: fc.Now().Add(-23 * time.Hour), 3743 Expires: fc.Now().Add(time.Hour), 3744 }, 3745 { 3746 Serial: "2", 3747 RegistrationID: expectedReg.Id, 3748 Created: fc.Now().Add(-22 * time.Hour), 3749 Expires: fc.Now().Add(2 * time.Hour), 3750 }, 3751 { 3752 Serial: "3", 3753 RegistrationID: expectedReg.Id, 3754 Created: fc.Now().Add(-23 * time.Hour), 3755 Expires: fc.Now().Add(-1 * time.Hour), 3756 }, 3757 } 3758 3759 for _, row := range inserts { 3760 err := sa.dbMap.Insert(context.Background(), &row) 3761 test.AssertNotError(t, err, "inserting test serial") 3762 } 3763 3764 // Expect the result stream to have two entries. 3765 res := make(chan *sapb.Serial) 3766 stream := &fakeServerStream[sapb.Serial]{output: res} 3767 var err error 3768 go func() { 3769 err = sa.GetSerialsByAccount(&sapb.RegistrationID{Id: expectedReg.Id}, stream) 3770 close(res) // Let our main test thread continue. 3771 }() 3772 3773 var seen []string 3774 for serial := range res { 3775 if !slices.Contains([]string{"1", "2"}, serial.Serial) { 3776 t.Errorf("Received unexpected serial %q", serial.Serial) 3777 } 3778 if slices.Contains(seen, serial.Serial) { 3779 t.Errorf("Received serial %q more than once", serial.Serial) 3780 } 3781 seen = append(seen, serial.Serial) 3782 } 3783 test.AssertNotError(t, err, "calling GetSerialsByAccount") 3784 test.AssertEquals(t, len(seen), 2) 3785 } 3786 3787 func TestUnpauseAccount(t *testing.T) { 3788 sa, _, cleanUp := initSA(t) 3789 defer cleanUp() 3790 3791 reg := createWorkingRegistration(t, sa) 3792 3793 tests := []struct { 3794 name string 3795 state []pausedModel 3796 req *sapb.RegistrationID 3797 }{ 3798 { 3799 name: "UnpauseAccount with no paused identifiers", 3800 state: nil, 3801 req: &sapb.RegistrationID{Id: reg.Id}, 3802 }, 3803 { 3804 name: "UnpauseAccount with one paused identifier", 3805 state: []pausedModel{ 3806 { 3807 RegistrationID: reg.Id, 3808 identifierModel: identifierModel{ 3809 Type: identifierTypeToUint[string(identifier.TypeDNS)], 3810 Value: "example.com", 3811 }, 3812 PausedAt: sa.clk.Now().Add(-time.Hour), 3813 }, 3814 }, 3815 req: &sapb.RegistrationID{Id: reg.Id}, 3816 }, 3817 { 3818 name: "UnpauseAccount with multiple paused identifiers", 3819 state: []pausedModel{ 3820 { 3821 RegistrationID: reg.Id, 3822 identifierModel: identifierModel{ 3823 Type: identifierTypeToUint[string(identifier.TypeDNS)], 3824 Value: "example.com", 3825 }, 3826 PausedAt: sa.clk.Now().Add(-time.Hour), 3827 }, 3828 { 3829 RegistrationID: reg.Id, 3830 identifierModel: identifierModel{ 3831 Type: identifierTypeToUint[string(identifier.TypeDNS)], 3832 Value: "example.net", 3833 }, 3834 PausedAt: sa.clk.Now().Add(-time.Hour), 3835 }, 3836 { 3837 RegistrationID: reg.Id, 3838 identifierModel: identifierModel{ 3839 Type: identifierTypeToUint[string(identifier.TypeDNS)], 3840 Value: "example.org", 3841 }, 3842 PausedAt: sa.clk.Now().Add(-time.Hour), 3843 }, 3844 }, 3845 req: &sapb.RegistrationID{Id: reg.Id}, 3846 }, 3847 } 3848 for _, tt := range tests { 3849 t.Run(tt.name, func(t *testing.T) { 3850 defer func() { 3851 _, err := sa.dbMap.ExecContext(ctx, "DELETE FROM paused WHERE 1 = 1") 3852 test.AssertNotError(t, err, "cleaning up paused table") 3853 }() 3854 3855 // Setup table state. 3856 for _, state := range tt.state { 3857 err := sa.dbMap.Insert(ctx, &state) 3858 test.AssertNotError(t, err, "inserting test identifier") 3859 } 3860 3861 _, err := sa.UnpauseAccount(ctx, tt.req) 3862 test.AssertNotError(t, err, "Unexpected error for UnpauseAccount()") 3863 3864 // Count the number of paused identifiers. 3865 var count int 3866 err = sa.dbReadOnlyMap.SelectOne( 3867 ctx, 3868 &count, 3869 "SELECT COUNT(*) FROM paused WHERE registrationID = ? AND unpausedAt IS NULL", 3870 tt.req.Id, 3871 ) 3872 test.AssertNotError(t, err, "SELECT COUNT(*) failed") 3873 test.AssertEquals(t, count, 0) 3874 }) 3875 } 3876 } 3877 3878 func bulkInsertPausedIdentifiers(ctx context.Context, sa *SQLStorageAuthority, regID int64, count int) error { 3879 const batchSize = 1000 3880 3881 values := make([]any, 0, batchSize*4) 3882 now := sa.clk.Now().Add(-time.Hour) 3883 batches := (count + batchSize - 1) / batchSize 3884 3885 for batch := range batches { 3886 query := ` 3887 INSERT INTO paused (registrationID, identifierType, identifierValue, pausedAt) 3888 VALUES` 3889 3890 start := batch * batchSize 3891 end := min(start+batchSize, count) 3892 3893 for i := start; i < end; i++ { 3894 if i > start { 3895 query += "," 3896 } 3897 query += "(?, ?, ?, ?)" 3898 values = append(values, regID, identifierTypeToUint[string(identifier.TypeDNS)], fmt.Sprintf("example%d.com", i), now) 3899 } 3900 3901 _, err := sa.dbMap.ExecContext(ctx, query, values...) 3902 if err != nil { 3903 return fmt.Errorf("bulk inserting paused identifiers: %w", err) 3904 } 3905 values = values[:0] 3906 } 3907 3908 return nil 3909 } 3910 3911 func TestUnpauseAccountWithTwoLoops(t *testing.T) { 3912 sa, _, cleanUp := initSA(t) 3913 defer cleanUp() 3914 3915 reg := createWorkingRegistration(t, sa) 3916 3917 err := bulkInsertPausedIdentifiers(ctx, sa, reg.Id, 12000) 3918 test.AssertNotError(t, err, "bulk inserting paused identifiers") 3919 3920 result, err := sa.UnpauseAccount(ctx, &sapb.RegistrationID{Id: reg.Id}) 3921 test.AssertNotError(t, err, "Unexpected error for UnpauseAccount()") 3922 test.AssertEquals(t, result.Count, int64(12000)) 3923 } 3924 3925 func TestUnpauseAccountWithMaxLoops(t *testing.T) { 3926 sa, _, cleanUp := initSA(t) 3927 defer cleanUp() 3928 3929 reg := createWorkingRegistration(t, sa) 3930 err := bulkInsertPausedIdentifiers(ctx, sa, reg.Id, 50001) 3931 test.AssertNotError(t, err, "bulk inserting paused identifiers") 3932 3933 result, err := sa.UnpauseAccount(ctx, &sapb.RegistrationID{Id: reg.Id}) 3934 test.AssertNotError(t, err, "Unexpected error for UnpauseAccount()") 3935 test.AssertEquals(t, result.Count, int64(50000)) 3936 } 3937 3938 func TestPauseIdentifiers(t *testing.T) { 3939 sa, _, cleanUp := initSA(t) 3940 defer cleanUp() 3941 3942 reg := createWorkingRegistration(t, sa) 3943 ptrTime := func(t time.Time) *time.Time { 3944 return &t 3945 } 3946 3947 fourWeeksAgo := sa.clk.Now().Add(-4 * 7 * 24 * time.Hour) 3948 threeWeeksAgo := sa.clk.Now().Add(-3 * 7 * 24 * time.Hour) 3949 3950 tests := []struct { 3951 name string 3952 state []pausedModel 3953 req *sapb.PauseRequest 3954 want *sapb.PauseIdentifiersResponse 3955 }{ 3956 { 3957 name: "An identifier which is not now or previously paused", 3958 state: nil, 3959 req: &sapb.PauseRequest{ 3960 RegistrationID: reg.Id, 3961 Identifiers: []*corepb.Identifier{ 3962 { 3963 Type: string(identifier.TypeDNS), 3964 Value: "example.com", 3965 }, 3966 }, 3967 }, 3968 want: &sapb.PauseIdentifiersResponse{ 3969 Paused: 1, 3970 Repaused: 0, 3971 }, 3972 }, 3973 { 3974 name: "One unpaused entry which was previously paused", 3975 state: []pausedModel{ 3976 { 3977 RegistrationID: reg.Id, 3978 identifierModel: identifierModel{ 3979 Type: identifierTypeToUint[string(identifier.TypeDNS)], 3980 Value: "example.com", 3981 }, 3982 PausedAt: fourWeeksAgo, 3983 UnpausedAt: ptrTime(threeWeeksAgo), 3984 }, 3985 }, 3986 req: &sapb.PauseRequest{ 3987 RegistrationID: reg.Id, 3988 Identifiers: []*corepb.Identifier{ 3989 { 3990 Type: string(identifier.TypeDNS), 3991 Value: "example.com", 3992 }, 3993 }, 3994 }, 3995 want: &sapb.PauseIdentifiersResponse{ 3996 Paused: 0, 3997 Repaused: 1, 3998 }, 3999 }, 4000 { 4001 name: "One unpaused entry which was previously paused and unpaused less than 2 weeks ago", 4002 state: []pausedModel{ 4003 { 4004 RegistrationID: reg.Id, 4005 identifierModel: identifierModel{ 4006 Type: identifierTypeToUint[string(identifier.TypeDNS)], 4007 Value: "example.com", 4008 }, 4009 PausedAt: fourWeeksAgo, 4010 UnpausedAt: ptrTime(sa.clk.Now().Add(-13 * 24 * time.Hour)), 4011 }, 4012 }, 4013 req: &sapb.PauseRequest{ 4014 RegistrationID: reg.Id, 4015 Identifiers: []*corepb.Identifier{ 4016 { 4017 Type: string(identifier.TypeDNS), 4018 Value: "example.com", 4019 }, 4020 }, 4021 }, 4022 want: &sapb.PauseIdentifiersResponse{ 4023 Paused: 0, 4024 Repaused: 0, 4025 }, 4026 }, 4027 { 4028 name: "An identifier which is currently paused", 4029 state: []pausedModel{ 4030 { 4031 RegistrationID: reg.Id, 4032 identifierModel: identifierModel{ 4033 Type: identifierTypeToUint[string(identifier.TypeDNS)], 4034 Value: "example.com", 4035 }, 4036 PausedAt: fourWeeksAgo, 4037 }, 4038 }, 4039 req: &sapb.PauseRequest{ 4040 RegistrationID: reg.Id, 4041 Identifiers: []*corepb.Identifier{ 4042 { 4043 Type: string(identifier.TypeDNS), 4044 Value: "example.com", 4045 }, 4046 }, 4047 }, 4048 want: &sapb.PauseIdentifiersResponse{ 4049 Paused: 0, 4050 Repaused: 0, 4051 }, 4052 }, 4053 { 4054 name: "Two previously paused entries and one new entry", 4055 state: []pausedModel{ 4056 { 4057 RegistrationID: reg.Id, 4058 identifierModel: identifierModel{ 4059 Type: identifierTypeToUint[string(identifier.TypeDNS)], 4060 Value: "example.com", 4061 }, 4062 PausedAt: fourWeeksAgo, 4063 UnpausedAt: ptrTime(threeWeeksAgo), 4064 }, 4065 { 4066 RegistrationID: reg.Id, 4067 identifierModel: identifierModel{ 4068 Type: identifierTypeToUint[string(identifier.TypeDNS)], 4069 Value: "example.net", 4070 }, 4071 PausedAt: fourWeeksAgo, 4072 UnpausedAt: ptrTime(threeWeeksAgo), 4073 }, 4074 }, 4075 req: &sapb.PauseRequest{ 4076 RegistrationID: reg.Id, 4077 Identifiers: []*corepb.Identifier{ 4078 { 4079 Type: string(identifier.TypeDNS), 4080 Value: "example.com", 4081 }, 4082 { 4083 Type: string(identifier.TypeDNS), 4084 Value: "example.net", 4085 }, 4086 { 4087 Type: string(identifier.TypeDNS), 4088 Value: "example.org", 4089 }, 4090 }, 4091 }, 4092 want: &sapb.PauseIdentifiersResponse{ 4093 Paused: 1, 4094 Repaused: 2, 4095 }, 4096 }, 4097 } 4098 for _, tt := range tests { 4099 t.Run(tt.name, func(t *testing.T) { 4100 defer func() { 4101 _, err := sa.dbMap.ExecContext(ctx, "DELETE FROM paused WHERE 1 = 1") 4102 test.AssertNotError(t, err, "cleaning up paused table") 4103 }() 4104 4105 // Setup table state. 4106 for _, state := range tt.state { 4107 err := sa.dbMap.Insert(ctx, &state) 4108 test.AssertNotError(t, err, "inserting test identifier") 4109 } 4110 4111 got, err := sa.PauseIdentifiers(ctx, tt.req) 4112 test.AssertNotError(t, err, "Unexpected error for PauseIdentifiers()") 4113 test.AssertEquals(t, got.Paused, tt.want.Paused) 4114 test.AssertEquals(t, got.Repaused, tt.want.Repaused) 4115 }) 4116 } 4117 } 4118 4119 func TestCheckIdentifiersPaused(t *testing.T) { 4120 sa, _, cleanUp := initSA(t) 4121 defer cleanUp() 4122 4123 ptrTime := func(t time.Time) *time.Time { 4124 return &t 4125 } 4126 4127 reg := createWorkingRegistration(t, sa) 4128 tests := []struct { 4129 name string 4130 state []pausedModel 4131 req *sapb.PauseRequest 4132 want *sapb.Identifiers 4133 }{ 4134 { 4135 name: "No paused identifiers", 4136 state: nil, 4137 req: &sapb.PauseRequest{ 4138 RegistrationID: reg.Id, 4139 Identifiers: []*corepb.Identifier{ 4140 { 4141 Type: string(identifier.TypeDNS), 4142 Value: "example.com", 4143 }, 4144 }, 4145 }, 4146 want: &sapb.Identifiers{ 4147 Identifiers: []*corepb.Identifier{}, 4148 }, 4149 }, 4150 { 4151 name: "One paused identifier", 4152 state: []pausedModel{ 4153 { 4154 RegistrationID: reg.Id, 4155 identifierModel: identifierModel{ 4156 Type: identifierTypeToUint[string(identifier.TypeDNS)], 4157 Value: "example.com", 4158 }, 4159 PausedAt: sa.clk.Now().Add(-time.Hour), 4160 }, 4161 }, 4162 req: &sapb.PauseRequest{ 4163 RegistrationID: reg.Id, 4164 Identifiers: []*corepb.Identifier{ 4165 { 4166 Type: string(identifier.TypeDNS), 4167 Value: "example.com", 4168 }, 4169 }, 4170 }, 4171 want: &sapb.Identifiers{ 4172 Identifiers: []*corepb.Identifier{ 4173 { 4174 Type: string(identifier.TypeDNS), 4175 Value: "example.com", 4176 }, 4177 }, 4178 }, 4179 }, 4180 { 4181 name: "Two paused identifiers, one unpaused", 4182 state: []pausedModel{ 4183 { 4184 RegistrationID: reg.Id, 4185 identifierModel: identifierModel{ 4186 Type: identifierTypeToUint[string(identifier.TypeDNS)], 4187 Value: "example.com", 4188 }, 4189 PausedAt: sa.clk.Now().Add(-time.Hour), 4190 }, 4191 { 4192 RegistrationID: reg.Id, 4193 identifierModel: identifierModel{ 4194 Type: identifierTypeToUint[string(identifier.TypeDNS)], 4195 Value: "example.net", 4196 }, 4197 PausedAt: sa.clk.Now().Add(-time.Hour), 4198 }, 4199 { 4200 RegistrationID: reg.Id, 4201 identifierModel: identifierModel{ 4202 Type: identifierTypeToUint[string(identifier.TypeDNS)], 4203 Value: "example.org", 4204 }, 4205 PausedAt: sa.clk.Now().Add(-time.Hour), 4206 UnpausedAt: ptrTime(sa.clk.Now().Add(-time.Minute)), 4207 }, 4208 }, 4209 req: &sapb.PauseRequest{ 4210 RegistrationID: reg.Id, 4211 Identifiers: []*corepb.Identifier{ 4212 { 4213 Type: string(identifier.TypeDNS), 4214 Value: "example.com", 4215 }, 4216 { 4217 Type: string(identifier.TypeDNS), 4218 Value: "example.net", 4219 }, 4220 { 4221 Type: string(identifier.TypeDNS), 4222 Value: "example.org", 4223 }, 4224 }, 4225 }, 4226 want: &sapb.Identifiers{ 4227 Identifiers: []*corepb.Identifier{ 4228 { 4229 Type: string(identifier.TypeDNS), 4230 Value: "example.com", 4231 }, 4232 { 4233 Type: string(identifier.TypeDNS), 4234 Value: "example.net", 4235 }, 4236 }, 4237 }, 4238 }, 4239 } 4240 for _, tt := range tests { 4241 t.Run(tt.name, func(t *testing.T) { 4242 defer func() { 4243 _, err := sa.dbMap.ExecContext(ctx, "DELETE FROM paused WHERE 1 = 1") 4244 test.AssertNotError(t, err, "cleaning up paused table") 4245 }() 4246 4247 // Setup table state. 4248 for _, state := range tt.state { 4249 err := sa.dbMap.Insert(ctx, &state) 4250 test.AssertNotError(t, err, "inserting test identifier") 4251 } 4252 4253 got, err := sa.CheckIdentifiersPaused(ctx, tt.req) 4254 test.AssertNotError(t, err, "Unexpected error for PauseIdentifiers()") 4255 test.AssertDeepEquals(t, got.Identifiers, tt.want.Identifiers) 4256 }) 4257 } 4258 } 4259 4260 func TestGetPausedIdentifiers(t *testing.T) { 4261 sa, _, cleanUp := initSA(t) 4262 defer cleanUp() 4263 4264 ptrTime := func(t time.Time) *time.Time { 4265 return &t 4266 } 4267 4268 reg := createWorkingRegistration(t, sa) 4269 4270 tests := []struct { 4271 name string 4272 state []pausedModel 4273 req *sapb.RegistrationID 4274 want *sapb.Identifiers 4275 }{ 4276 { 4277 name: "No paused identifiers", 4278 state: nil, 4279 req: &sapb.RegistrationID{Id: reg.Id}, 4280 want: &sapb.Identifiers{ 4281 Identifiers: []*corepb.Identifier{}, 4282 }, 4283 }, 4284 { 4285 name: "One paused identifier", 4286 state: []pausedModel{ 4287 { 4288 RegistrationID: reg.Id, 4289 identifierModel: identifierModel{ 4290 Type: identifierTypeToUint[string(identifier.TypeDNS)], 4291 Value: "example.com", 4292 }, 4293 PausedAt: sa.clk.Now().Add(-time.Hour), 4294 }, 4295 }, 4296 req: &sapb.RegistrationID{Id: reg.Id}, 4297 want: &sapb.Identifiers{ 4298 Identifiers: []*corepb.Identifier{ 4299 { 4300 Type: string(identifier.TypeDNS), 4301 Value: "example.com", 4302 }, 4303 }, 4304 }, 4305 }, 4306 { 4307 name: "Two paused identifiers, one unpaused", 4308 state: []pausedModel{ 4309 { 4310 RegistrationID: reg.Id, 4311 identifierModel: identifierModel{ 4312 Type: identifierTypeToUint[string(identifier.TypeDNS)], 4313 Value: "example.com", 4314 }, 4315 PausedAt: sa.clk.Now().Add(-time.Hour), 4316 }, 4317 { 4318 RegistrationID: reg.Id, 4319 identifierModel: identifierModel{ 4320 Type: identifierTypeToUint[string(identifier.TypeDNS)], 4321 Value: "example.net", 4322 }, 4323 PausedAt: sa.clk.Now().Add(-time.Hour), 4324 }, 4325 { 4326 RegistrationID: reg.Id, 4327 identifierModel: identifierModel{ 4328 Type: identifierTypeToUint[string(identifier.TypeDNS)], 4329 Value: "example.org", 4330 }, 4331 PausedAt: sa.clk.Now().Add(-time.Hour), 4332 UnpausedAt: ptrTime(sa.clk.Now().Add(-time.Minute)), 4333 }, 4334 }, 4335 req: &sapb.RegistrationID{Id: reg.Id}, 4336 want: &sapb.Identifiers{ 4337 Identifiers: []*corepb.Identifier{ 4338 { 4339 Type: string(identifier.TypeDNS), 4340 Value: "example.com", 4341 }, 4342 { 4343 Type: string(identifier.TypeDNS), 4344 Value: "example.net", 4345 }, 4346 }, 4347 }, 4348 }, 4349 } 4350 for _, tt := range tests { 4351 t.Run(tt.name, func(t *testing.T) { 4352 defer func() { 4353 _, err := sa.dbMap.ExecContext(ctx, "DELETE FROM paused WHERE 1 = 1") 4354 test.AssertNotError(t, err, "cleaning up paused table") 4355 }() 4356 4357 // Setup table state. 4358 for _, state := range tt.state { 4359 err := sa.dbMap.Insert(ctx, &state) 4360 test.AssertNotError(t, err, "inserting test identifier") 4361 } 4362 4363 got, err := sa.GetPausedIdentifiers(ctx, tt.req) 4364 test.AssertNotError(t, err, "Unexpected error for PauseIdentifiers()") 4365 test.AssertDeepEquals(t, got.Identifiers, tt.want.Identifiers) 4366 }) 4367 } 4368 } 4369 4370 func TestGetPausedIdentifiersOnlyUnpausesOneAccount(t *testing.T) { 4371 sa, _, cleanUp := initSA(t) 4372 defer cleanUp() 4373 4374 reg1 := createWorkingRegistration(t, sa) 4375 reg2, err := sa.NewRegistration(ctx, &corepb.Registration{ 4376 Key: newAcctKey(t), 4377 CreatedAt: mustTimestamp("2018-04-01 07:00"), 4378 Status: string(core.StatusValid), 4379 }) 4380 test.AssertNotError(t, err, "creating second registration") 4381 4382 // Insert two paused identifiers for two different accounts. 4383 err = sa.dbMap.Insert(ctx, &pausedModel{ 4384 RegistrationID: reg1.Id, 4385 identifierModel: identifierModel{ 4386 Type: identifierTypeToUint[string(identifier.TypeDNS)], 4387 Value: "example.com", 4388 }, 4389 PausedAt: sa.clk.Now().Add(-time.Hour), 4390 }) 4391 test.AssertNotError(t, err, "inserting test identifier") 4392 4393 err = sa.dbMap.Insert(ctx, &pausedModel{ 4394 RegistrationID: reg2.Id, 4395 identifierModel: identifierModel{ 4396 Type: identifierTypeToUint[string(identifier.TypeDNS)], 4397 Value: "example.net", 4398 }, 4399 PausedAt: sa.clk.Now().Add(-time.Hour), 4400 }) 4401 test.AssertNotError(t, err, "inserting test identifier") 4402 4403 // Unpause the first account. 4404 _, err = sa.UnpauseAccount(ctx, &sapb.RegistrationID{Id: reg1.Id}) 4405 test.AssertNotError(t, err, "UnpauseAccount failed") 4406 4407 // Check that the second account's identifier is still paused. 4408 idents, err := sa.GetPausedIdentifiers(ctx, &sapb.RegistrationID{Id: reg2.Id}) 4409 test.AssertNotError(t, err, "GetPausedIdentifiers failed") 4410 test.AssertEquals(t, len(idents.Identifiers), 1) 4411 test.AssertEquals(t, idents.Identifiers[0].Value, "example.net") 4412 } 4413 4414 func newAcctKey(t *testing.T) []byte { 4415 key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 4416 jwk := &jose.JSONWebKey{Key: key.Public()} 4417 acctKey, err := jwk.MarshalJSON() 4418 test.AssertNotError(t, err, "failed to marshal account key") 4419 return acctKey 4420 } 4421 4422 func TestUpdateRegistrationKey(t *testing.T) { 4423 sa, _, cleanUp := initSA(t) 4424 defer cleanUp() 4425 4426 _, err := sa.UpdateRegistrationKey(ctx, &sapb.UpdateRegistrationKeyRequest{}) 4427 test.AssertError(t, err, "should not have been able to update registration key without a registration ID") 4428 test.AssertContains(t, err.Error(), "incomplete gRPC request message") 4429 4430 existingReg, err := sa.NewRegistration(ctx, &corepb.Registration{ 4431 Key: newAcctKey(t), 4432 }) 4433 test.AssertNotError(t, err, "creating new registration") 4434 4435 tests := []struct { 4436 name string 4437 newJwk []byte 4438 expectedError string 4439 }{ 4440 { 4441 name: "update a valid registration with a new account key", 4442 newJwk: newAcctKey(t), 4443 }, 4444 { 4445 name: "update a valid registration with a duplicate account key", 4446 newJwk: existingReg.Key, 4447 expectedError: "key is already in use for a different account", 4448 }, 4449 { 4450 name: "update a valid registration with a malformed account key", 4451 newJwk: []byte("Eat at Joe's"), 4452 expectedError: "parsing JWK", 4453 }, 4454 } 4455 for _, tt := range tests { 4456 t.Run(tt.name, func(t *testing.T) { 4457 reg, err := sa.NewRegistration(ctx, &corepb.Registration{ 4458 Key: newAcctKey(t), 4459 }) 4460 test.AssertNotError(t, err, "creating new registration") 4461 4462 updatedReg, err := sa.UpdateRegistrationKey(ctx, &sapb.UpdateRegistrationKeyRequest{ 4463 RegistrationID: reg.Id, 4464 Jwk: tt.newJwk, 4465 }) 4466 if tt.expectedError != "" { 4467 test.AssertError(t, err, "should have errored") 4468 test.AssertContains(t, err.Error(), tt.expectedError) 4469 } else { 4470 test.AssertNotError(t, err, "unexpected error for UpdateRegistrationKey()") 4471 test.AssertEquals(t, updatedReg.Id, reg.Id) 4472 test.AssertDeepEquals(t, updatedReg.Key, tt.newJwk) 4473 4474 refetchedReg, err := sa.GetRegistration(ctx, &sapb.RegistrationID{ 4475 Id: reg.Id, 4476 }) 4477 test.AssertNotError(t, err, "retrieving registration") 4478 test.AssertDeepEquals(t, refetchedReg.Key, tt.newJwk) 4479 } 4480 }) 4481 } 4482 } 4483 4484 type mockRLOStream struct { 4485 grpc.ServerStream 4486 sent []*sapb.RateLimitOverrideResponse 4487 ctx context.Context 4488 } 4489 4490 func newMockRLOStream() *mockRLOStream { 4491 return &mockRLOStream{ctx: ctx} 4492 } 4493 func (m *mockRLOStream) Context() context.Context { return m.ctx } 4494 func (m *mockRLOStream) RecvMsg(any) error { return io.EOF } 4495 func (m *mockRLOStream) Send(ov *sapb.RateLimitOverrideResponse) error { 4496 m.sent = append(m.sent, ov) 4497 return nil 4498 } 4499 4500 func TestAddRateLimitOverrideInsertThenUpdate(t *testing.T) { 4501 if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" { 4502 // TODO(#8147): Remove this skip. 4503 t.Skip("skipping, this overrides table must exist for this test to run") 4504 } 4505 4506 sa, _, cleanup := initSA(t) 4507 defer cleanup() 4508 4509 expectBucketKey := core.RandomString(10) 4510 ov := &sapb.RateLimitOverride{ 4511 LimitEnum: 1, 4512 BucketKey: expectBucketKey, 4513 Comment: "insert", 4514 Period: durationpb.New(time.Hour), 4515 Count: 100, 4516 Burst: 100, 4517 } 4518 4519 // Insert 4520 resp, err := sa.AddRateLimitOverride(ctx, &sapb.AddRateLimitOverrideRequest{Override: ov}) 4521 test.AssertNotError(t, err, "expected successful insert, got error") 4522 test.Assert(t, resp.Inserted && resp.Enabled, fmt.Sprintf("expected (Inserted=true, Enabled=true) for initial insert, got (%v,%v)", resp.Inserted, resp.Enabled)) 4523 4524 // Update (change comment) 4525 ov.Comment = "updated" 4526 resp, err = sa.AddRateLimitOverride(ctx, &sapb.AddRateLimitOverrideRequest{Override: ov}) 4527 test.AssertNotError(t, err, "expected successful update, got error") 4528 test.Assert(t, !resp.Inserted && resp.Enabled, fmt.Sprintf("expected (Inserted=false, Enabled=true) for update, got (%v, %v)", resp.Inserted, resp.Enabled)) 4529 4530 got, err := sa.GetRateLimitOverride(ctx, &sapb.GetRateLimitOverrideRequest{LimitEnum: 1, BucketKey: expectBucketKey}) 4531 test.AssertNotError(t, err, "expected GetRateLimitOverride to succeed, got error") 4532 test.AssertEquals(t, got.Override.Comment, "updated") 4533 4534 // Disable 4535 _, err = sa.DisableRateLimitOverride(ctx, &sapb.DisableRateLimitOverrideRequest{LimitEnum: 1, BucketKey: expectBucketKey}) 4536 test.AssertNotError(t, err, "expected DisableRateLimitOverride to succeed, got error") 4537 4538 // Update and check that it's still disabled. 4539 got, err = sa.GetRateLimitOverride(ctx, &sapb.GetRateLimitOverrideRequest{LimitEnum: 1, BucketKey: expectBucketKey}) 4540 test.AssertNotError(t, err, "expected GetRateLimitOverride to succeed, got error") 4541 test.Assert(t, !got.Enabled, fmt.Sprintf("expected Enabled=false after disable, got Enabled=%v", got.Enabled)) 4542 4543 // Update (change period, count, and burst) 4544 ov.Period = durationpb.New(2 * time.Hour) 4545 ov.Count = 200 4546 ov.Burst = 200 4547 _, err = sa.AddRateLimitOverride(ctx, &sapb.AddRateLimitOverrideRequest{Override: ov}) 4548 test.AssertNotError(t, err, "expected successful update, got error") 4549 4550 got, err = sa.GetRateLimitOverride(ctx, &sapb.GetRateLimitOverrideRequest{LimitEnum: 1, BucketKey: expectBucketKey}) 4551 test.AssertNotError(t, err, "expected GetRateLimitOverride to succeed, got error") 4552 test.AssertEquals(t, got.Override.Period.AsDuration(), 2*time.Hour) 4553 test.AssertEquals(t, got.Override.Count, int64(200)) 4554 test.AssertEquals(t, got.Override.Burst, int64(200)) 4555 } 4556 4557 func TestDisableEnableRateLimitOverride(t *testing.T) { 4558 if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" { 4559 // TODO(#8147): Remove this skip. 4560 t.Skip("skipping, this overrides table must exist for this test to run") 4561 } 4562 4563 sa, _, cleanup := initSA(t) 4564 defer cleanup() 4565 4566 expectBucketKey := core.RandomString(10) 4567 ov := &sapb.RateLimitOverride{ 4568 LimitEnum: 2, 4569 BucketKey: expectBucketKey, 4570 Period: durationpb.New(time.Hour), 4571 Count: 1, 4572 Burst: 1, 4573 Comment: "test", 4574 } 4575 _, _ = sa.AddRateLimitOverride(ctx, &sapb.AddRateLimitOverrideRequest{Override: ov}) 4576 4577 // Disable 4578 _, err := sa.DisableRateLimitOverride(ctx, 4579 &sapb.DisableRateLimitOverrideRequest{LimitEnum: 2, BucketKey: expectBucketKey}) 4580 test.AssertNotError(t, err, "expected DisableRateLimitOverride to succeed, got error") 4581 4582 st, _ := sa.GetRateLimitOverride(ctx, 4583 &sapb.GetRateLimitOverrideRequest{LimitEnum: 2, BucketKey: expectBucketKey}) 4584 test.Assert(t, !st.Enabled, 4585 fmt.Sprintf("expected Enabled=false after disable, got Enabled=%v", st.Enabled)) 4586 4587 // Enable 4588 _, err = sa.EnableRateLimitOverride(ctx, 4589 &sapb.EnableRateLimitOverrideRequest{LimitEnum: 2, BucketKey: expectBucketKey}) 4590 test.AssertNotError(t, err, "expected EnableRateLimitOverride to succeed, got error") 4591 4592 st, _ = sa.GetRateLimitOverride(ctx, 4593 &sapb.GetRateLimitOverrideRequest{LimitEnum: 2, BucketKey: expectBucketKey}) 4594 test.Assert(t, st.Enabled, 4595 fmt.Sprintf("expected Enabled=true after enable, got Enabled=%v", st.Enabled)) 4596 } 4597 4598 func TestGetEnabledRateLimitOverrides(t *testing.T) { 4599 if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" { 4600 // TODO(#8147): Remove this skip. 4601 t.Skip("skipping, this overrides table must exist for this test to run") 4602 } 4603 4604 sa, _, cleanup := initSA(t) 4605 defer cleanup() 4606 4607 // Enabled 4608 ov1 := &sapb.RateLimitOverride{ 4609 LimitEnum: 10, BucketKey: "on", Period: durationpb.New(time.Second), Count: 1, Burst: 1, Comment: "on", 4610 } 4611 // Disabled 4612 ov2 := &sapb.RateLimitOverride{ 4613 LimitEnum: 11, BucketKey: "off", Period: durationpb.New(time.Second), Count: 1, Burst: 1, Comment: "off", 4614 } 4615 4616 _, err := sa.AddRateLimitOverride(ctx, &sapb.AddRateLimitOverrideRequest{Override: ov1}) 4617 test.AssertNotError(t, err, "expected successful insert of ov1, got error") 4618 _, err = sa.AddRateLimitOverride(ctx, &sapb.AddRateLimitOverrideRequest{Override: ov2}) 4619 test.AssertNotError(t, err, "expected successful insert of ov2, got error") 4620 _, err = sa.DisableRateLimitOverride(ctx, &sapb.DisableRateLimitOverrideRequest{LimitEnum: 11, BucketKey: "off"}) 4621 test.AssertNotError(t, err, "expected DisableRateLimitOverride of ov2 to succeed, got error") 4622 _, err = sa.EnableRateLimitOverride(ctx, &sapb.EnableRateLimitOverrideRequest{LimitEnum: 10, BucketKey: "on"}) 4623 test.AssertNotError(t, err, "expected EnableRateLimitOverride of ov1 to succeed, got error") 4624 4625 stream := newMockRLOStream() 4626 err = sa.GetEnabledRateLimitOverrides(&emptypb.Empty{}, stream) 4627 test.AssertNotError(t, err, "expected streaming enabled overrides to succeed, got error") 4628 test.AssertEquals(t, len(stream.sent), 1) 4629 test.AssertEquals(t, stream.sent[0].Override.BucketKey, "on") 4630 }