func TestValidateContacts(t *testing.T) { _, _, ra, _, cleanUp := initAuthorities(t) defer cleanUp() tel, _ := core.ParseAcmeURL("tel:") ansible, _ := core.ParseAcmeURL("ansible:earth.sol.milkyway.laniakea/letsencrypt") validEmail, _ := core.ParseAcmeURL("mailto:[email protected]") malformedEmail, _ := core.ParseAcmeURL("mailto:admin.com") err := ra.validateContacts([]*core.AcmeURL{}) test.AssertNotError(t, err, "No Contacts") err = ra.validateContacts([]*core.AcmeURL{tel, validEmail}) test.AssertError(t, err, "Too Many Contacts") err = ra.validateContacts([]*core.AcmeURL{tel}) test.AssertNotError(t, err, "Simple Telephone") err = ra.validateContacts([]*core.AcmeURL{validEmail}) test.AssertNotError(t, err, "Valid Email") err = ra.validateContacts([]*core.AcmeURL{malformedEmail}) test.AssertError(t, err, "Malformed Email") err = ra.validateContacts([]*core.AcmeURL{ansible}) test.AssertError(t, err, "Unknown scheme") }
func TestAddRegistration(t *testing.T) { sa, clk, cleanUp := initSA(t) defer cleanUp() jwk := satest.GoodJWK() contact, err := core.ParseAcmeURL("mailto:[email protected]") if err != nil { t.Fatalf("unable to parse contact link: %s", err) } contacts := []*core.AcmeURL{contact} reg, err := sa.NewRegistration(core.Registration{ Key: jwk, Contact: contacts, InitialIP: net.ParseIP("43.34.43.34"), }) if err != nil { t.Fatalf("Couldn't create new registration: %s", err) } test.Assert(t, reg.ID != 0, "ID shouldn't be 0") test.AssertDeepEquals(t, reg.Contact, contacts) _, err = sa.GetRegistration(0) test.AssertError(t, err, "Registration object for ID 0 was returned") dbReg, err := sa.GetRegistration(reg.ID) test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.ID)) expectedReg := core.Registration{ ID: reg.ID, Key: jwk, InitialIP: net.ParseIP("43.34.43.34"), CreatedAt: clk.Now(), } test.AssertEquals(t, dbReg.ID, expectedReg.ID) test.Assert(t, core.KeyDigestEquals(dbReg.Key, expectedReg.Key), "Stored key != expected") u, _ := core.ParseAcmeURL("test.com") newReg := core.Registration{ ID: reg.ID, Key: jwk, Contact: []*core.AcmeURL{u}, InitialIP: net.ParseIP("72.72.72.72"), Agreement: "yes", } err = sa.UpdateRegistration(newReg) test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.ID)) dbReg, err = sa.GetRegistrationByKey(jwk) test.AssertNotError(t, err, "Couldn't get registration by key") test.AssertEquals(t, dbReg.ID, newReg.ID) test.AssertEquals(t, dbReg.Agreement, newReg.Agreement) var anotherJWK jose.JsonWebKey err = json.Unmarshal([]byte(anotherKey), &anotherJWK) test.AssertNotError(t, err, "couldn't unmarshal anotherJWK") _, err = sa.GetRegistrationByKey(anotherJWK) test.AssertError(t, err, "Registration object for invalid key was returned") }
func TestValidateContacts(t *testing.T) { _, _, ra, _, cleanUp := initAuthorities(t) defer cleanUp() ansible, _ := core.ParseAcmeURL("ansible:earth.sol.milkyway.laniakea/letsencrypt") validEmail, _ := core.ParseAcmeURL("mailto:[email protected]") otherValidEmail, _ := core.ParseAcmeURL("mailto:[email protected]") malformedEmail, _ := core.ParseAcmeURL("mailto:admin.com") err := ra.validateContacts(context.Background(), &[]*core.AcmeURL{}) test.AssertNotError(t, err, "No Contacts") err = ra.validateContacts(context.Background(), &[]*core.AcmeURL{validEmail, otherValidEmail}) test.AssertError(t, err, "Too Many Contacts") err = ra.validateContacts(context.Background(), &[]*core.AcmeURL{validEmail}) test.AssertNotError(t, err, "Valid Email") err = ra.validateContacts(context.Background(), &[]*core.AcmeURL{malformedEmail}) test.AssertError(t, err, "Malformed Email") err = ra.validateContacts(context.Background(), &[]*core.AcmeURL{ansible}) test.AssertError(t, err, "Unknown scheme") err = ra.validateContacts(context.Background(), &[]*core.AcmeURL{nil}) test.AssertError(t, err, "Nil AcmeURL") }
func TestValidateContacts(t *testing.T) { tel, _ := core.ParseAcmeURL("tel:") ansible, _ := core.ParseAcmeURL("ansible:earth.sol.milkyway.laniakea/letsencrypt") validEmail, _ := core.ParseAcmeURL("mailto:[email protected]") invalidEmail, _ := core.ParseAcmeURL("mailto:[email protected]") malformedEmail, _ := core.ParseAcmeURL("mailto:admin.com") err := validateContacts([]*core.AcmeURL{}, &mocks.MockDNS{}) test.AssertNotError(t, err, "No Contacts") err = validateContacts([]*core.AcmeURL{tel}, &mocks.MockDNS{}) test.AssertNotError(t, err, "Simple Telephone") err = validateContacts([]*core.AcmeURL{validEmail}, &mocks.MockDNS{}) test.AssertNotError(t, err, "Valid Email") err = validateContacts([]*core.AcmeURL{invalidEmail}, &mocks.MockDNS{}) test.AssertError(t, err, "Invalid Email") err = validateContacts([]*core.AcmeURL{malformedEmail}, &mocks.MockDNS{}) test.AssertError(t, err, "Malformed Email") err = validateContacts([]*core.AcmeURL{ansible}, &mocks.MockDNS{}) test.AssertError(t, err, "Unknown scehme") }
func TestSendNags(t *testing.T) { stats, _ := statsd.NewNoopClient(nil) mc := mocks.Mailer{} rs := newFakeRegStore() fc := newFakeClock(t) m := mailer{ stats: stats, mailer: &mc, emailTemplate: tmpl, rs: rs, clk: fc, } cert := &x509.Certificate{ Subject: pkix.Name{ CommonName: "happy", }, NotAfter: fc.Now().AddDate(0, 0, 2), DNSNames: []string{"example.com"}, } email, _ := core.ParseAcmeURL("mailto:[email protected]") emailB, _ := core.ParseAcmeURL("mailto:[email protected]") err := m.sendNags([]*core.AcmeURL{email}, []*x509.Certificate{cert}) test.AssertNotError(t, err, "Failed to send warning messages") test.AssertEquals(t, len(mc.Messages), 1) test.AssertEquals(t, fmt.Sprintf(`hi, cert for DNS names example.com is going to expire in 2 days (%s)`, cert.NotAfter.Format(time.RFC822Z)), mc.Messages[0]) mc.Clear() err = m.sendNags([]*core.AcmeURL{email, emailB}, []*x509.Certificate{cert}) test.AssertNotError(t, err, "Failed to send warning messages") test.AssertEquals(t, len(mc.Messages), 2) test.AssertEquals(t, fmt.Sprintf(`hi, cert for DNS names example.com is going to expire in 2 days (%s)`, cert.NotAfter.Format(time.RFC822Z)), mc.Messages[0]) test.AssertEquals(t, fmt.Sprintf(`hi, cert for DNS names example.com is going to expire in 2 days (%s)`, cert.NotAfter.Format(time.RFC822Z)), mc.Messages[1]) mc.Clear() err = m.sendNags([]*core.AcmeURL{}, []*x509.Certificate{cert}) test.AssertNotError(t, err, "Not an error to pass no email contacts") test.AssertEquals(t, len(mc.Messages), 0) templates, err := template.ParseGlob("../../data/*.template") test.AssertNotError(t, err, "Failed to parse templates") for _, template := range templates.Templates() { m.emailTemplate = template err = m.sendNags(nil, []*x509.Certificate{cert}) test.AssertNotError(t, err, "failed to send nag") } }
func CreateDomainAuthWithRegId(t *testing.T, domainName string, sa *SQLStorageAuthority, regID int64) (authz core.Authorization) { // create pending auth authz, err := sa.NewPendingAuthorization(core.Authorization{RegistrationID: regID, Challenges: []core.Challenge{core.Challenge{}}}) if err != nil { t.Fatalf("Couldn't create new pending authorization: %s", err) } test.Assert(t, authz.ID != "", "ID shouldn't be blank") // prepare challenge for auth u, err := core.ParseAcmeURL(domainName) test.AssertNotError(t, err, "Couldn't parse domainName "+domainName) chall := core.Challenge{Type: "simpleHttp", Status: core.StatusValid, URI: u, Token: "THISWOULDNTBEAGOODTOKEN"} combos := make([][]int, 1) combos[0] = []int{0, 1} exp := time.Now().AddDate(0, 0, 1) // expire in 1 day // validate pending auth authz.Status = core.StatusPending authz.Identifier = core.AcmeIdentifier{Type: core.IdentifierDNS, Value: domainName} authz.Expires = &exp authz.Challenges = []core.Challenge{chall} authz.Combinations = combos // save updated auth err = sa.UpdatePendingAuthorization(authz) test.AssertNotError(t, err, "Couldn't update pending authorization with ID "+authz.ID) return }
func mustParseAcmeURL(acmeURL string) *core.AcmeURL { c, err := core.ParseAcmeURL(acmeURL) if err != nil { panic(fmt.Sprintf("unable to parse as AcmeURL %#v: %s", acmeURL, err)) } return c }
func TestNewRegistration(t *testing.T) { _, sa, ra, _, cleanUp := initAuthorities(t) defer cleanUp() mailto, _ := core.ParseAcmeURL("mailto:[email protected]") input := core.Registration{ Contact: []*core.AcmeURL{mailto}, Key: AccountKeyB, InitialIP: net.ParseIP("7.6.6.5"), } result, err := ra.NewRegistration(input) if err != nil { t.Fatalf("could not create new registration: %s", err) } test.Assert(t, core.KeyDigestEquals(result.Key, AccountKeyB), "Key didn't match") test.Assert(t, len(result.Contact) == 1, "Wrong number of contacts") test.Assert(t, mailto.String() == result.Contact[0].String(), "Contact didn't match") test.Assert(t, result.Agreement == "", "Agreement didn't default empty") reg, err := sa.GetRegistration(result.ID) test.AssertNotError(t, err, "Failed to retrieve registration") test.Assert(t, core.KeyDigestEquals(reg.Key, AccountKeyB), "Retrieved registration differed.") }
func TestNewRegistrationNoFieldOverwrite(t *testing.T) { _, _, _, ra, cleanUp := initAuthorities(t) defer cleanUp() mailto, _ := core.ParseAcmeURL("mailto:[email protected]") input := core.Registration{ ID: 23, Key: AccountKeyC, Contact: []*core.AcmeURL{mailto}, Agreement: "I agreed", } result, err := ra.NewRegistration(input) test.AssertNotError(t, err, "Could not create new registration") test.Assert(t, result.ID != 23, "ID shouldn't be set by user") // TODO: Enable this test case once we validate terms agreement. //test.Assert(t, result.Agreement != "I agreed", "Agreement shouldn't be set with invalid URL") id := result.ID result2, err := ra.UpdateRegistration(result, core.Registration{ ID: 33, Key: ShortKey, }) test.AssertNotError(t, err, "Could not update registration") test.Assert(t, result2.ID != 33, fmt.Sprintf("ID shouldn't be overwritten. expected %d, got %d", id, result2.ID)) test.Assert(t, !core.KeyDigestEquals(result2.Key, ShortKey), "Key shouldn't be overwritten") }
func TestDontFindRevokedCert(t *testing.T) { expiresIn := 24 * time.Hour ctx := setup(t, []time.Duration{expiresIn}) var keyA jose.JsonWebKey err := json.Unmarshal(jsonKeyA, &keyA) test.AssertNotError(t, err, "Failed to unmarshal public JWK") emailA, _ := core.ParseAcmeURL("mailto:[email protected]") regA := core.Registration{ ID: 1, Contact: []*core.AcmeURL{ emailA, }, Key: keyA, InitialIP: net.ParseIP("6.5.5.6"), } regA, err = ctx.ssa.NewRegistration(regA) if err != nil { t.Fatalf("Couldn't store regA: %s", err) } rawCertA := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy A", }, NotAfter: ctx.fc.Now().Add(expiresIn), DNSNames: []string{"example-a.com"}, SerialNumber: big.NewInt(1337), } certDerA, _ := x509.CreateCertificate(rand.Reader, &rawCertA, &rawCertA, &testKey.PublicKey, &testKey) certA := &core.Certificate{ RegistrationID: regA.ID, Serial: "001", Expires: rawCertA.NotAfter, DER: certDerA, } certStatusA := &core.CertificateStatus{ Serial: "001", Status: core.OCSPStatusRevoked, } setupDBMap, err := sa.NewDbMap("mysql+tcp://test_setup@localhost:3306/boulder_sa_test") err = setupDBMap.Insert(certA) test.AssertNotError(t, err, "unable to insert Certificate") err = setupDBMap.Insert(certStatusA) test.AssertNotError(t, err, "unable to insert CertificateStatus") err = ctx.m.findExpiringCertificates() test.AssertNotError(t, err, "err from findExpiringCertificates") if len(ctx.mc.Messages) != 0 { t.Errorf("no emails should have been sent, but sent %d", len(ctx.mc.Messages)) } }
func TestSendNags(t *testing.T) { stats, _ := statsd.NewNoopClient(nil) mc := mockMail{} rs := newFakeRegStore() fc := clock.NewFake() fc.Add(7 * 24 * time.Hour) m := mailer{ stats: stats, mailer: &mc, emailTemplate: tmpl, rs: rs, clk: fc, } cert := &x509.Certificate{ Subject: pkix.Name{ CommonName: "happy", }, NotAfter: fc.Now().AddDate(0, 0, 2), DNSNames: []string{"example.com"}, } email, _ := core.ParseAcmeURL("mailto:[email protected]") emailB, _ := core.ParseAcmeURL("mailto:[email protected]") err := m.sendNags(cert, []*core.AcmeURL{email}) test.AssertNotError(t, err, "Failed to send warning messages") test.AssertEquals(t, len(mc.Messages), 1) test.AssertEquals(t, fmt.Sprintf(`hi, cert for DNS names example.com is going to expire in 3 days (%s)`, cert.NotAfter), mc.Messages[0]) mc.Clear() err = m.sendNags(cert, []*core.AcmeURL{email, emailB}) test.AssertNotError(t, err, "Failed to send warning messages") test.AssertEquals(t, len(mc.Messages), 2) test.AssertEquals(t, fmt.Sprintf(`hi, cert for DNS names example.com is going to expire in 3 days (%s)`, cert.NotAfter), mc.Messages[0]) test.AssertEquals(t, fmt.Sprintf(`hi, cert for DNS names example.com is going to expire in 3 days (%s)`, cert.NotAfter), mc.Messages[1]) mc.Clear() err = m.sendNags(cert, []*core.AcmeURL{}) test.AssertNotError(t, err, "Not an error to pass no email contacts") test.AssertEquals(t, len(mc.Messages), 0) }
func TestNewRegistrationBadKey(t *testing.T) { _, _, _, ra, cleanUp := initAuthorities(t) defer cleanUp() mailto, _ := core.ParseAcmeURL("mailto:[email protected]") input := core.Registration{ Contact: []*core.AcmeURL{mailto}, Key: ShortKey, } _, err := ra.NewRegistration(input) test.AssertError(t, err, "Should have rejected authorization with short key") }
// CreateWorkingRegistration inserts a new, correct Registration into // SA using GoodKey under the hood. This a hack to allow both the CA // and SA tests to benefit because the CA tests currently require a // full-fledged SQLSAImpl. Long term, when the CA tests no longer need // CreateWorkingRegistration, this and CreateWorkingRegistration can // be pushed back into the SA tests proper. func CreateWorkingRegistration(t *testing.T, sa core.StorageAuthority) core.Registration { contact, err := core.ParseAcmeURL("mailto:[email protected]") if err != nil { t.Fatalf("unable to parse contact link: %s", err) } contacts := []*core.AcmeURL{contact} reg, err := sa.NewRegistration(core.Registration{ Key: GoodJWK(), Contact: contacts, }) if err != nil { t.Fatalf("Unable to create new registration") } return reg }
func modelToChallenge(cm *challModel) (core.Challenge, error) { c := core.Challenge{ Type: cm.Type, Status: cm.Status, Validated: cm.Validated, Token: cm.Token, TLS: cm.TLS, } if len(cm.URI) > 0 { uri, err := core.ParseAcmeURL(cm.URI) if err != nil { return core.Challenge{}, err } c.URI = uri } if len(cm.Validation) > 0 { val, err := jose.ParseSigned(string(cm.Validation)) if err != nil { return core.Challenge{}, err } c.Validation = val } if len(cm.Error) > 0 { var problem core.ProblemDetails err := json.Unmarshal(cm.Error, &problem) if err != nil { return core.Challenge{}, err } c.Error = &problem } if len(cm.ValidationRecord) > 0 { var vr []core.ValidationRecord err := json.Unmarshal(cm.ValidationRecord, &vr) if err != nil { return core.Challenge{}, err } c.ValidationRecord = vr } if len(cm.AccountKey) > 0 { var ak jose.JsonWebKey err := json.Unmarshal(cm.AccountKey, &ak) if err != nil { return core.Challenge{}, err } c.AccountKey = &ak } return c, nil }
// CreateWorkingRegistration inserts a new, correct Registration into // SA using GoodKey under the hood. This a hack to allow both the CA // and SA tests to benefit because the CA tests currently require a // full-fledged SQLSAImpl. Long term, when the CA tests no longer need // CreateWorkingRegistration, this and CreateWorkingRegistration can // be pushed back into the SA tests proper. func CreateWorkingRegistration(t *testing.T, sa core.StorageAdder) core.Registration { contact, err := core.ParseAcmeURL("mailto:[email protected]") if err != nil { t.Fatalf("unable to parse contact link: %s", err) } contacts := &[]*core.AcmeURL{contact} reg, err := sa.NewRegistration(context.Background(), core.Registration{ Key: GoodJWK(), Contact: contacts, InitialIP: net.ParseIP("88.77.66.11"), CreatedAt: time.Date(2003, 5, 10, 0, 0, 0, 0, time.UTC), }) if err != nil { t.Fatalf("Unable to create new registration: %s", err) } return reg }
func TestLifetimeOfACert(t *testing.T) { ctx := setup(t, []time.Duration{time.Hour * 24, time.Hour * 24 * 4, time.Hour * 24 * 7}) defer ctx.cleanUp() var keyA jose.JsonWebKey err := json.Unmarshal(jsonKeyA, &keyA) test.AssertNotError(t, err, "Failed to unmarshal public JWK") emailA, _ := core.ParseAcmeURL("mailto:[email protected]") regA := core.Registration{ ID: 1, Contact: []*core.AcmeURL{ emailA, }, Key: keyA, InitialIP: net.ParseIP("1.2.2.1"), } regA, err = ctx.ssa.NewRegistration(regA) if err != nil { t.Fatalf("Couldn't store regA: %s", err) } rawCertA := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy A", }, NotAfter: ctx.fc.Now(), DNSNames: []string{"example-a.com"}, SerialNumber: big.NewInt(1337), } certDerA, _ := x509.CreateCertificate(rand.Reader, &rawCertA, &rawCertA, &testKey.PublicKey, &testKey) certA := &core.Certificate{ RegistrationID: regA.ID, Serial: "001", Expires: rawCertA.NotAfter, DER: certDerA, } certStatusA := &core.CertificateStatus{ Serial: "001", Status: core.OCSPStatusGood, } setupDBMap, err := sa.NewDbMap("mysql+tcp://test_setup@localhost:3306/boulder_sa_test") err = setupDBMap.Insert(certA) test.AssertNotError(t, err, "unable to insert Certificate") err = setupDBMap.Insert(certStatusA) test.AssertNotError(t, err, "unable to insert CertificateStatus") type lifeTest struct { timeLeft time.Duration numMsgs int context string } tests := []lifeTest{ { timeLeft: 9 * 24 * time.Hour, // 9 days before expiration numMsgs: 0, context: "Expected no emails sent because we are more than 7 days out.", }, { (7*24 + 12) * time.Hour, // 7.5 days before 1, "Sent 1 for 7 day notice.", }, { 7 * 24 * time.Hour, 1, "The 7 day email was already sent.", }, { (4*24 - 1) * time.Hour, // <4 days before, the mailer did not run yesterday 2, "Sent 1 for the 7 day notice, and 1 for the 4 day notice.", }, { 36 * time.Hour, // within 1day + nagMargin 3, "Sent 1 for the 7 day notice, 1 for the 4 day notice, and 1 for the 1 day notice.", }, { 12 * time.Hour, 3, "The 1 day before email was already sent.", }, { -2 * 24 * time.Hour, // 2 days after expiration 3, "No expiration warning emails are sent after expiration", }, } for _, tt := range tests { ctx.fc.Add(-tt.timeLeft) err = ctx.m.findExpiringCertificates() test.AssertNotError(t, err, "error calling findExpiringCertificates") if len(ctx.mc.Messages) != tt.numMsgs { t.Errorf(tt.context+" number of messages: expected %d, got %d", tt.numMsgs, len(ctx.mc.Messages)) } ctx.fc.Add(tt.timeLeft) } }
func TestFindExpiringCertificates(t *testing.T) { ctx := setup(t, []time.Duration{time.Hour * 24, time.Hour * 24 * 4, time.Hour * 24 * 7}) log.Clear() err := ctx.m.findExpiringCertificates() test.AssertNotError(t, err, "Failed on no certificates") test.AssertEquals(t, len(log.GetAllMatching("Searching for certificates that expire between.*")), 3) // Add some expiring certificates and registrations emailA, _ := core.ParseAcmeURL("mailto:[email protected]") emailB, _ := core.ParseAcmeURL("mailto:[email protected]") var keyA jose.JsonWebKey var keyB jose.JsonWebKey err = json.Unmarshal(jsonKeyA, &keyA) test.AssertNotError(t, err, "Failed to unmarshal public JWK") err = json.Unmarshal(jsonKeyB, &keyB) test.AssertNotError(t, err, "Failed to unmarshal public JWK") regA := core.Registration{ ID: 1, Contact: []*core.AcmeURL{ emailA, }, Key: keyA, InitialIP: net.ParseIP("2.3.2.3"), } regB := core.Registration{ ID: 2, Contact: []*core.AcmeURL{ emailB, }, Key: keyB, InitialIP: net.ParseIP("2.3.2.3"), } regA, err = ctx.ssa.NewRegistration(regA) if err != nil { t.Fatalf("Couldn't store regA: %s", err) } regB, err = ctx.ssa.NewRegistration(regB) if err != nil { t.Fatalf("Couldn't store regB: %s", err) } // Expires in <1d, last nag was the 4d nag rawCertA := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy A", }, NotAfter: ctx.fc.Now().Add(23 * time.Hour), DNSNames: []string{"example-a.com"}, SerialNumber: big.NewInt(1337), } certDerA, _ := x509.CreateCertificate(rand.Reader, &rawCertA, &rawCertA, &testKey.PublicKey, &testKey) certA := &core.Certificate{ RegistrationID: regA.ID, Serial: "001", Expires: rawCertA.NotAfter, DER: certDerA, } certStatusA := &core.CertificateStatus{ Serial: "001", LastExpirationNagSent: ctx.fc.Now().AddDate(0, 0, -3), Status: core.OCSPStatusGood, } // Expires in 3d, already sent 4d nag at 4.5d rawCertB := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy B", }, NotAfter: ctx.fc.Now().AddDate(0, 0, 3), DNSNames: []string{"example-b.com"}, SerialNumber: big.NewInt(1337), } certDerB, _ := x509.CreateCertificate(rand.Reader, &rawCertB, &rawCertB, &testKey.PublicKey, &testKey) certB := &core.Certificate{ RegistrationID: regA.ID, Serial: "002", Expires: rawCertB.NotAfter, DER: certDerB, } certStatusB := &core.CertificateStatus{ Serial: "002", LastExpirationNagSent: ctx.fc.Now().Add(-36 * time.Hour), Status: core.OCSPStatusGood, } // Expires in 7d and change, no nag sent at all yet rawCertC := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy C", }, NotAfter: ctx.fc.Now().Add((7*24 + 1) * time.Hour), DNSNames: []string{"example-c.com"}, SerialNumber: big.NewInt(1337), } certDerC, _ := x509.CreateCertificate(rand.Reader, &rawCertC, &rawCertC, &testKey.PublicKey, &testKey) certC := &core.Certificate{ RegistrationID: regB.ID, Serial: "003", Expires: rawCertC.NotAfter, DER: certDerC, } certStatusC := &core.CertificateStatus{ Serial: "003", Status: core.OCSPStatusGood, } setupDBMap, err := sa.NewDbMap("mysql+tcp://test_setup@localhost:3306/boulder_sa_test") err = setupDBMap.Insert(certA) test.AssertNotError(t, err, "Couldn't add certA") err = setupDBMap.Insert(certB) test.AssertNotError(t, err, "Couldn't add certB") err = setupDBMap.Insert(certC) test.AssertNotError(t, err, "Couldn't add certC") err = setupDBMap.Insert(certStatusA) test.AssertNotError(t, err, "Couldn't add certStatusA") err = setupDBMap.Insert(certStatusB) test.AssertNotError(t, err, "Couldn't add certStatusB") err = setupDBMap.Insert(certStatusC) test.AssertNotError(t, err, "Couldn't add certStatusC") log.Clear() err = ctx.m.findExpiringCertificates() test.AssertNotError(t, err, "Failed to find expiring certs") // Should get 001 and 003 test.AssertEquals(t, len(ctx.mc.Messages), 2) test.AssertEquals(t, fmt.Sprintf(`hi, cert for DNS names example-a.com is going to expire in 0 days (%s)`, rawCertA.NotAfter.UTC().Format("2006-01-02 15:04:05 -0700 MST")), ctx.mc.Messages[0]) test.AssertEquals(t, fmt.Sprintf(`hi, cert for DNS names example-c.com is going to expire in 7 days (%s)`, rawCertC.NotAfter.UTC().Format("2006-01-02 15:04:05 -0700 MST")), ctx.mc.Messages[1]) // A consecutive run shouldn't find anything ctx.mc.Clear() log.Clear() err = ctx.m.findExpiringCertificates() test.AssertNotError(t, err, "Failed to find expiring certs") test.AssertEquals(t, len(ctx.mc.Messages), 0) }
func TestFindExpiringCertificates(t *testing.T) { ctx := setup(t, []time.Duration{time.Hour * 24, time.Hour * 24 * 4, time.Hour * 24 * 7}) ctx.fc.Add(7 * 24 * time.Hour) log.Clear() err := ctx.m.findExpiringCertificates() test.AssertNotError(t, err, "Failed on no certificates") test.AssertEquals(t, len(log.GetAllMatching("Searching for certificates that expire between.*")), 3) // Add some expiring certificates and registrations emailA, _ := core.ParseAcmeURL("mailto:[email protected]") emailB, _ := core.ParseAcmeURL("mailto:[email protected]") var keyA jose.JsonWebKey var keyB jose.JsonWebKey err = json.Unmarshal(jsonKeyA, &keyA) test.AssertNotError(t, err, "Failed to unmarshal public JWK") err = json.Unmarshal(jsonKeyB, &keyB) test.AssertNotError(t, err, "Failed to unmarshal public JWK") regA := core.Registration{ ID: 1, Contact: []*core.AcmeURL{ emailA, }, Key: keyA, } regB := core.Registration{ ID: 2, Contact: []*core.AcmeURL{ emailB, }, Key: keyB, } regA, err = ctx.ssa.NewRegistration(regA) if err != nil { t.Fatalf("Couldn't store regA: %s", err) } regB, err = ctx.ssa.NewRegistration(regB) if err != nil { t.Fatalf("Couldn't store regB: %s", err) } rawCertA := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy A", }, // This is slightly within the ultime nag window (one day) NotAfter: ctx.fc.Now().AddDate(0, 0, 1).Add(-time.Hour), DNSNames: []string{"example-a.com"}, SerialNumber: big.NewInt(1337), } certDerA, _ := x509.CreateCertificate(rand.Reader, &rawCertA, &rawCertA, &testKey.PublicKey, &testKey) certA := &core.Certificate{ RegistrationID: regA.ID, Serial: "001", Expires: rawCertA.NotAfter, DER: certDerA, } // Already sent a nag but too long ago certStatusA := &core.CertificateStatus{ Serial: "001", LastExpirationNagSent: ctx.fc.Now().Add(-time.Hour * 24 * 3), Status: core.OCSPStatusGood, } rawCertB := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy B", }, NotAfter: ctx.fc.Now().AddDate(0, 0, 3), DNSNames: []string{"example-b.com"}, SerialNumber: big.NewInt(1337), } certDerB, _ := x509.CreateCertificate(rand.Reader, &rawCertB, &rawCertB, &testKey.PublicKey, &testKey) certB := &core.Certificate{ RegistrationID: regA.ID, Serial: "002", Expires: rawCertB.NotAfter, DER: certDerB, } // Already sent a nag for this period certStatusB := &core.CertificateStatus{ Serial: "002", LastExpirationNagSent: ctx.fc.Now().Add(-time.Hour * 24 * 3), Status: core.OCSPStatusGood, } rawCertC := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy C", }, // This is within the earliest nag window (7 days) NotAfter: ctx.fc.Now().AddDate(0, 0, 6), DNSNames: []string{"example-c.com"}, SerialNumber: big.NewInt(1337), } certDerC, _ := x509.CreateCertificate(rand.Reader, &rawCertC, &rawCertC, &testKey.PublicKey, &testKey) certC := &core.Certificate{ RegistrationID: regB.ID, Serial: "003", Expires: rawCertC.NotAfter, DER: certDerC, } certStatusC := &core.CertificateStatus{ Serial: "003", Status: core.OCSPStatusGood, } err = ctx.dbMap.Insert(certA) test.AssertNotError(t, err, "Couldn't add certA") err = ctx.dbMap.Insert(certB) test.AssertNotError(t, err, "Couldn't add certB") err = ctx.dbMap.Insert(certC) test.AssertNotError(t, err, "Couldn't add certC") err = ctx.dbMap.Insert(certStatusA) test.AssertNotError(t, err, "Couldn't add certStatusA") err = ctx.dbMap.Insert(certStatusB) test.AssertNotError(t, err, "Couldn't add certStatusB") err = ctx.dbMap.Insert(certStatusC) test.AssertNotError(t, err, "Couldn't add certStatusC") log.Clear() err = ctx.m.findExpiringCertificates() test.AssertNotError(t, err, "Failed to find expiring certs") // Should get 001 and 003 test.AssertEquals(t, len(ctx.mc.Messages), 2) test.AssertEquals(t, fmt.Sprintf(`hi, cert for DNS names example-a.com is going to expire in 1 days (%s)`, rawCertA.NotAfter.UTC().Format("2006-01-02 15:04:05 -0700 MST")), ctx.mc.Messages[0]) test.AssertEquals(t, fmt.Sprintf(`hi, cert for DNS names example-c.com is going to expire in 7 days (%s)`, rawCertC.NotAfter.UTC().Format("2006-01-02 15:04:05 -0700 MST")), ctx.mc.Messages[1]) // A consecutive run shouldn't find anything ctx.mc.Clear() log.Clear() err = ctx.m.findExpiringCertificates() test.AssertNotError(t, err, "Failed to find expiring certs") test.AssertEquals(t, len(ctx.mc.Messages), 0) }
// NewAuthorization constuct a new Authz from a request. func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int64) (authz core.Authorization, err error) { reg, err := ra.SA.GetRegistration(regID) if err != nil { err = core.MalformedRequestError(fmt.Sprintf("Invalid registration ID: %d", regID)) return authz, err } identifier := request.Identifier // Check that the identifier is present and appropriate if err = ra.PA.WillingToIssue(identifier); err != nil { err = core.UnauthorizedError(err.Error()) return authz, err } // Check CAA records for the requested identifier present, valid, err := ra.VA.CheckCAARecords(identifier) if err != nil { return authz, err } // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ra.log.Audit(fmt.Sprintf("Checked CAA records for %s, registration ID %d [Present: %t, Valid for issuance: %t]", identifier.Value, regID, present, valid)) if !valid { err = errors.New("CAA check for identifier failed") return authz, err } // Create validations, but we have to update them with URIs later challenges, combinations := ra.PA.ChallengesFor(identifier) // Partially-filled object authz = core.Authorization{ Identifier: identifier, RegistrationID: regID, Status: core.StatusPending, Combinations: combinations, Challenges: challenges, } // Get a pending Auth first so we can get our ID back, then update with challenges authz, err = ra.SA.NewPendingAuthorization(authz) if err != nil { // InternalServerError since the user-data was validated before being // passed to the SA. err = core.InternalServerError(fmt.Sprintf("Invalid authorization request: %s", err)) return authz, err } // Construct all the challenge URIs for i := range authz.Challenges { // Ignoring these errors because we construct the URLs to be correct challengeURI, _ := core.ParseAcmeURL(ra.AuthzBase + authz.ID + "?challenge=" + strconv.Itoa(i)) authz.Challenges[i].URI = challengeURI // Add the account key used to generate the challenge authz.Challenges[i].AccountKey = ®.Key if !authz.Challenges[i].IsSane(false) { // InternalServerError because we generated these challenges, they should // be OK. err = core.InternalServerError(fmt.Sprintf("Challenge didn't pass sanity check: %+v", authz.Challenges[i])) return authz, err } } // Store the authorization object, then return it err = ra.SA.UpdatePendingAuthorization(authz) if err != nil { // InternalServerError because we created the authorization just above, // and adding Sane challenges should not break it. err = core.InternalServerError(err.Error()) } return authz, err }
func TestFindExpiringCertificates(t *testing.T) { dbMap, err := sa.NewDbMap(dbConnStr) if err != nil { t.Fatalf("Couldn't connect the database: %s", err) } cleanUp := test.ResetTestDatabase(t, dbMap.Db) ssa, err := sa.NewSQLStorageAuthority(dbMap) if err != nil { t.Fatalf("unable to create SQLStorageAuthority: %s", err) } defer cleanUp() tmpl, err := template.New("expiry-email").Parse(testTmpl) test.AssertNotError(t, err, "Couldn't parse test email template") stats, _ := statsd.NewNoopClient(nil) mc := mockMail{} m := mailer{ log: blog.GetAuditLogger(), stats: stats, mailer: &mc, emailTemplate: tmpl, dbMap: dbMap, rs: ssa, nagTimes: []time.Duration{time.Hour * 24, time.Hour * 24 * 4, time.Hour * 24 * 7}, limit: 100, } log.Clear() err = m.findExpiringCertificates() test.AssertNotError(t, err, "Failed on no certificates") test.AssertEquals(t, len(log.GetAllMatching("Searching for certificates that expire between.*")), 3) // Add some expiring certificates and registrations emailA, _ := core.ParseAcmeURL("mailto:[email protected]") emailB, _ := core.ParseAcmeURL("mailto:[email protected]") var keyA jose.JsonWebKey var keyB jose.JsonWebKey err = json.Unmarshal(jsonKeyA, &keyA) test.AssertNotError(t, err, "Failed to unmarshal public JWK") err = json.Unmarshal(jsonKeyB, &keyB) test.AssertNotError(t, err, "Failed to unmarshal public JWK") regA := core.Registration{ ID: 1, Contact: []*core.AcmeURL{ emailA, }, Key: keyA, } regB := core.Registration{ ID: 2, Contact: []*core.AcmeURL{ emailB, }, Key: keyB, } regA, err = ssa.NewRegistration(regA) if err != nil { t.Fatalf("Couldn't store regA: %s", err) } regB, err = ssa.NewRegistration(regB) if err != nil { t.Fatalf("Couldn't store regB: %s", err) } rawCertA := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy A", }, NotAfter: time.Now().AddDate(0, 0, 1), DNSNames: []string{"example-a.com"}, SerialNumber: big.NewInt(1337), } certDerA, _ := x509.CreateCertificate(rand.Reader, &rawCertA, &rawCertA, &testKey.PublicKey, &testKey) certA := &core.Certificate{ RegistrationID: regA.ID, Status: core.StatusValid, Serial: "001", Expires: time.Now().AddDate(0, 0, 1), DER: certDerA, } // Already sent a nag but too long ago certStatusA := &core.CertificateStatus{Serial: "001", LastExpirationNagSent: time.Now().Add(-time.Hour * 24 * 3)} rawCertB := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy B", }, NotAfter: time.Now().AddDate(0, 0, 3), DNSNames: []string{"example-b.com"}, SerialNumber: big.NewInt(1337), } certDerB, _ := x509.CreateCertificate(rand.Reader, &rawCertB, &rawCertB, &testKey.PublicKey, &testKey) certB := &core.Certificate{ RegistrationID: regA.ID, Status: core.StatusValid, Serial: "002", Expires: time.Now().AddDate(0, 0, 3), DER: certDerB, } // Already sent a nag for this period certStatusB := &core.CertificateStatus{Serial: "002", LastExpirationNagSent: time.Now().Add(-time.Hour * 24 * 3)} rawCertC := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy C", }, NotAfter: time.Now().AddDate(0, 0, 7), DNSNames: []string{"example-c.com"}, SerialNumber: big.NewInt(1337), } certDerC, _ := x509.CreateCertificate(rand.Reader, &rawCertC, &rawCertC, &testKey.PublicKey, &testKey) certC := &core.Certificate{ RegistrationID: regB.ID, Status: core.StatusValid, Serial: "003", Expires: time.Now().AddDate(0, 0, 7), DER: certDerC, } certStatusC := &core.CertificateStatus{Serial: "003"} err = dbMap.Insert(certA) test.AssertNotError(t, err, "Couldn't add certA") err = dbMap.Insert(certB) test.AssertNotError(t, err, "Couldn't add certB") err = dbMap.Insert(certC) test.AssertNotError(t, err, "Couldn't add certC") err = dbMap.Insert(certStatusA) test.AssertNotError(t, err, "Couldn't add certStatusA") err = dbMap.Insert(certStatusB) test.AssertNotError(t, err, "Couldn't add certStatusB") err = dbMap.Insert(certStatusC) test.AssertNotError(t, err, "Couldn't add certStatusC") log.Clear() err = m.findExpiringCertificates() test.AssertNotError(t, err, "Failed to find expiring certs") // Should get 001 and 003 test.AssertEquals(t, len(mc.Messages), 2) test.AssertEquals(t, fmt.Sprintf(`hi, cert for DNS names example-a.com is going to expire in 1 days (%s)`, rawCertA.NotAfter.UTC().Format("2006-01-02 15:04:05 -0700 MST")), mc.Messages[0]) test.AssertEquals(t, fmt.Sprintf(`hi, cert for DNS names example-c.com is going to expire in 7 days (%s)`, rawCertC.NotAfter.UTC().Format("2006-01-02 15:04:05 -0700 MST")), mc.Messages[1]) // A consecutive run shouldn't find anything mc.Clear() log.Clear() err = m.findExpiringCertificates() test.AssertNotError(t, err, "Failed to find expiring certs") test.AssertEquals(t, len(mc.Messages), 0) }
ft, err := time.Parse(fakeTimeFormat, fakeTimeFormat) if err != nil { t.Fatal(err) } fc := clock.NewFake() fc.Set(ft.UTC()) return fc } const testTmpl = `hi, cert for DNS names {{.DNSNames}} is going to expire in {{.DaysToExpiration}} days ({{.ExpirationDate}})` const testEmailSubject = `email subject for test` const emailARaw = "*****@*****.**" const emailBRaw = "*****@*****.**" var ( emailA, _ = core.ParseAcmeURL("mailto:" + emailARaw) emailB, _ = core.ParseAcmeURL("mailto:" + emailBRaw) jsonKeyA = []byte(`{ "kty":"RSA", "n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", "e":"AQAB" }`) jsonKeyB = []byte(`{ "kty":"RSA", "n":"z8bp-jPtHt4lKBqepeKF28g_QAEOuEsCIou6sZ9ndsQsEjxEOQxQ0xNOQezsKa63eogw8YS3vzjUcPP5BJuVzfPfGd5NVUdT-vSSwxk3wvk_jtNqhrpcoG0elRPQfMVsQWmxCAXCVRz3xbcFI8GTe-syynG3l-g1IzYIIZVNI6jdljCZML1HOMTTW4f7uJJ8mM-08oQCeHbr5ejK7O2yMSSYxW03zY-Tj1iVEebROeMv6IEEJNFSS4yM-hLpNAqVuQxFGetwtwjDMC1Drs1dTWrPuUAAjKGrP151z1_dE74M5evpAhZUmpKv1hY-x85DC6N0hFPgowsanmTNNiV75w", "e":"AAEAAQ" }`) jsonKeyC = []byte(`{ "kty":"RSA", "n":"rFH5kUBZrlPj73epjJjyCxzVzZuV--JjKgapoqm9pOuOt20BUTdHqVfC2oDclqM7HFhkkX9OSJMTHgZ7WaVqZv9u1X2yjdx9oVmMLuspX7EytW_ZKDZSzL-sCOFCuQAuYKkLbsdcA3eHBK_lwc4zwdeHFMKIulNvLqckkqYB9s8GpgNXBDIQ8GjR5HuJke_WUNjYHSd8jY1LU9swKWsLQe2YoQUz_ekQvBvBCoaFEtrtRaSJKNLIVDObXFr2TLIiFiM0Em90kK01-eQ7ZiruZTKomll64bRFPoNo4_uwubddg3xTqur2vdF3NyhTrYdvAgTem4uC0PFjEQ1bK_djBQ", "e":"AQAB"
func (c testCtx) addRegistrations(t *testing.T) { emailA, _ := core.ParseAcmeURL("mailto:" + emailARaw) emailB, _ := core.ParseAcmeURL("mailto:" + emailBRaw) emailC, _ := core.ParseAcmeURL("mailto:" + emailCRaw) tel, _ := core.ParseAcmeURL("tel:" + telNum) // Every registration needs a unique JOSE key jsonKeyA := []byte(`{ "kty":"RSA", "n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", "e":"AQAB" }`) jsonKeyB := []byte(`{ "kty":"RSA", "n":"z8bp-jPtHt4lKBqepeKF28g_QAEOuEsCIou6sZ9ndsQsEjxEOQxQ0xNOQezsKa63eogw8YS3vzjUcPP5BJuVzfPfGd5NVUdT-vSSwxk3wvk_jtNqhrpcoG0elRPQfMVsQWmxCAXCVRz3xbcFI8GTe-syynG3l-g1IzYIIZVNI6jdljCZML1HOMTTW4f7uJJ8mM-08oQCeHbr5ejK7O2yMSSYxW03zY-Tj1iVEebROeMv6IEEJNFSS4yM-hLpNAqVuQxFGetwtwjDMC1Drs1dTWrPuUAAjKGrP151z1_dE74M5evpAhZUmpKv1hY-x85DC6N0hFPgowsanmTNNiV75w", "e":"AAEAAQ" }`) jsonKeyC := []byte(`{ "kty":"RSA", "n":"rFH5kUBZrlPj73epjJjyCxzVzZuV--JjKgapoqm9pOuOt20BUTdHqVfC2oDclqM7HFhkkX9OSJMTHgZ7WaVqZv9u1X2yjdx9oVmMLuspX7EytW_ZKDZSzL-sCOFCuQAuYKkLbsdcA3eHBK_lwc4zwdeHFMKIulNvLqckkqYB9s8GpgNXBDIQ8GjR5HuJke_WUNjYHSd8jY1LU9swKWsLQe2YoQUz_ekQvBvBCoaFEtrtRaSJKNLIVDObXFr2TLIiFiM0Em90kK01-eQ7ZiruZTKomll64bRFPoNo4_uwubddg3xTqur2vdF3NyhTrYdvAgTem4uC0PFjEQ1bK_djBQ", "e":"AQAB" }`) jsonKeyD := []byte(`{ "kty":"RSA", "n":"rFH5kUBZrlPj73epjJjyCxzVzZuV--JjKgapoqm9pOuOt20BUTdHqVfC2oDclqM7HFhkkX9OSJMTHgZ7WaVqZv9u1X2yjdx9oVmMLuspX7EytW_ZKDZSzL-FCOFCuQAuYKkLbsdcA3eHBK_lwc4zwdeHFMKIulNvLqckkqYB9s8GpgNXBDIQ8GjR5HuJke_WUNjYHSd8jY1LU9swKWsLQe2YoQUz_ekQvBvBCoaFEtrtRaSJKNLIVDObXFr2TLIiFiM0Em90kK01-eQ7ZiruZTKomll64bRFPoNo4_uwubddg3xTqur2vdF3NyhTrYdvAgTem4uC0PFjEQ1bK_djBQ", "e":"AQAB" }`) var keyA jose.JsonWebKey var keyB jose.JsonWebKey var keyC jose.JsonWebKey var keyD jose.JsonWebKey err := json.Unmarshal(jsonKeyA, &keyA) test.AssertNotError(t, err, "Failed to unmarshal public JWK") err = json.Unmarshal(jsonKeyB, &keyB) test.AssertNotError(t, err, "Failed to unmarshal public JWK") err = json.Unmarshal(jsonKeyC, &keyC) test.AssertNotError(t, err, "Failed to unmarshal public JWK") err = json.Unmarshal(jsonKeyD, &keyD) test.AssertNotError(t, err, "Failed to unmarshal public JWK") // Regs A through C have `mailto:` contact ACME URL's regA = core.Registration{ ID: 1, Contact: &[]*core.AcmeURL{ emailA, }, Key: keyA, InitialIP: net.ParseIP("127.0.0.1"), } regB = core.Registration{ ID: 2, Contact: &[]*core.AcmeURL{ emailB, }, Key: keyB, InitialIP: net.ParseIP("127.0.0.1"), } regC = core.Registration{ ID: 3, Contact: &[]*core.AcmeURL{ emailC, }, Key: keyC, InitialIP: net.ParseIP("127.0.0.1"), } // Reg D has a `tel:` contact ACME URL regD = core.Registration{ ID: 4, Contact: &[]*core.AcmeURL{ tel, }, Key: keyD, InitialIP: net.ParseIP("127.0.0.1"), } // Add the four test registrations ctx := context.Background() regA, err = c.ssa.NewRegistration(ctx, regA) if err != nil { t.Fatalf("Couldn't store regA: %s", err) } regB, err = c.ssa.NewRegistration(ctx, regB) if err != nil { t.Fatalf("Couldn't store regB: %s", err) } regC, err = c.ssa.NewRegistration(ctx, regC) if err != nil { t.Fatalf("Couldn't store regC: %s", err) } regD, err = c.ssa.NewRegistration(ctx, regD) if err != nil { t.Fatalf("Couldn't store regD: %s", err) } }
func TestLifetimeOfACert(t *testing.T) { dbMap, err := sa.NewDbMap(dbConnStr) if err != nil { t.Fatalf("Couldn't connect the database: %s", err) } cleanUp := test.ResetTestDatabase(t, dbMap.Db) ssa, err := sa.NewSQLStorageAuthority(dbMap) if err != nil { t.Fatalf("unable to create SQLStorageAuthority: %s", err) } defer cleanUp() stats, _ := statsd.NewNoopClient(nil) mc := mockMail{} fc := clock.NewFake() m := mailer{ log: blog.GetAuditLogger(), stats: stats, mailer: &mc, emailTemplate: tmpl, dbMap: dbMap, rs: ssa, nagTimes: []time.Duration{time.Hour * 24, time.Hour * 24 * 4, time.Hour * 24 * 7}, limit: 100, clk: fc, } var keyA jose.JsonWebKey err = json.Unmarshal(jsonKeyA, &keyA) test.AssertNotError(t, err, "Failed to unmarshal public JWK") emailA, _ := core.ParseAcmeURL("mailto:[email protected]") regA := core.Registration{ ID: 1, Contact: []*core.AcmeURL{ emailA, }, Key: keyA, } regA, err = ssa.NewRegistration(regA) if err != nil { t.Fatalf("Couldn't store regA: %s", err) } rawCertA := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy A", }, NotAfter: fc.Now(), DNSNames: []string{"example-a.com"}, SerialNumber: big.NewInt(1337), } certDerA, _ := x509.CreateCertificate(rand.Reader, &rawCertA, &rawCertA, &testKey.PublicKey, &testKey) certA := &core.Certificate{ RegistrationID: regA.ID, Status: core.StatusValid, Serial: "001", Expires: rawCertA.NotAfter, DER: certDerA, } certStatusA := &core.CertificateStatus{ Serial: "001", } err = dbMap.Insert(certA) test.AssertNotError(t, err, "unable to insert Certificate") err = dbMap.Insert(certStatusA) test.AssertNotError(t, err, "unable to insert CertificateStatus") type lifeTest struct { timeLeft time.Duration numMsgs int context string } tests := []lifeTest{ { timeLeft: 8 * 24 * time.Hour, // 8 days before expiration numMsgs: 0, context: "Expected no emails sent because we are more than 7 days out.", }, { 7 * 24 * time.Hour, // 7 days before 1, "Sent 1 for 7 day notice.", }, { 5 * 24 * time.Hour, 1, "The 7 day email was already sent.", }, { 3 * 24 * time.Hour, // 3 days before, the mailer wasn't run the day before 2, "Sent 1 for the 7 day notice, and 1 for the 4 day notice.", }, { 1 * 24 * time.Hour, 3, "Sent 1 for the 7 day notice, 1 for the 4 day notice, and 1 for the 1 day notice.", }, { 12 * time.Hour, 3, "The 1 day before email was already sent.", }, { -2 * 24 * time.Hour, // 2 days after expiration 3, "No expiration warning emails are sent after expiration", }, } for _, tt := range tests { fc.Add(-tt.timeLeft) err = m.findExpiringCertificates() test.AssertNotError(t, err, "error calling findExpiringCertificates") if len(mc.Messages) != tt.numMsgs { t.Errorf(tt.context+" number of messages: expected %d, got %d", tt.numMsgs, len(mc.Messages)) } fc.Add(tt.timeLeft) } }