correct bound checking of expirations

The expiration mailer doesn't send email when the expiration is exactly
as far away as one of the "nag" times.

Adds a test for the bound checking behavior.
This commit is contained in:
Jeff Hodges 2015-08-28 21:44:15 -07:00
parent d4df15f604
commit 417158117e
2 changed files with 136 additions and 10 deletions

View File

@ -167,7 +167,7 @@ func (m *mailer) findExpiringCertificates() error {
JOIN certificateStatus AS cs
ON cs.serial = cert.serial
AND cert.expires > :cutoffA
AND cert.expires < :cutoffB
AND cert.expires <= :cutoffB
AND cert.status != "revoked"
AND cs.lastExpirationNagSent <= :nagCutoff
ORDER BY cert.expires ASC

View File

@ -74,22 +74,22 @@ func newFakeRegStore() fakeRegStore {
const testTmpl = `hi, cert for DNS names {{.DNSNames}} is going to expire in {{.DaysToExpiration}} days ({{.ExpirationDate}})`
var jsonKeyA = []byte(`{
var (
jsonKeyA = []byte(`{
"kty":"RSA",
"n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
"e":"AQAB"
}`)
var jsonKeyB = []byte(`{
jsonKeyB = []byte(`{
"kty":"RSA",
"n":"z8bp-jPtHt4lKBqepeKF28g_QAEOuEsCIou6sZ9ndsQsEjxEOQxQ0xNOQezsKa63eogw8YS3vzjUcPP5BJuVzfPfGd5NVUdT-vSSwxk3wvk_jtNqhrpcoG0elRPQfMVsQWmxCAXCVRz3xbcFI8GTe-syynG3l-g1IzYIIZVNI6jdljCZML1HOMTTW4f7uJJ8mM-08oQCeHbr5ejK7O2yMSSYxW03zY-Tj1iVEebROeMv6IEEJNFSS4yM-hLpNAqVuQxFGetwtwjDMC1Drs1dTWrPuUAAjKGrP151z1_dE74M5evpAhZUmpKv1hY-x85DC6N0hFPgowsanmTNNiV75w",
"e":"AAEAAQ"
}`)
var log = mocks.UseMockLog()
log = mocks.UseMockLog()
tmpl = template.Must(template.New("expiry-email").Parse(testTmpl))
)
func TestSendNags(t *testing.T) {
tmpl, err := template.New("expiry-email").Parse(testTmpl)
test.AssertNotError(t, err, "Couldn't parse test email template")
stats, _ := statsd.NewNoopClient(nil)
mc := mockMail{}
rs := newFakeRegStore()
@ -115,7 +115,7 @@ func TestSendNags(t *testing.T) {
email, _ := core.ParseAcmeURL("mailto:rolandshoemaker@gmail.com")
emailB, _ := core.ParseAcmeURL("mailto:test@gmail.com")
err = m.sendNags(cert, []*core.AcmeURL{email})
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])
@ -158,8 +158,7 @@ func TestFindExpiringCertificates(t *testing.T) {
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{}
fc := clock.NewFake()
@ -304,3 +303,130 @@ func TestFindExpiringCertificates(t *testing.T) {
test.AssertNotError(t, err, "Failed to find expiring certs")
test.AssertEquals(t, len(mc.Messages), 0)
}
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:one@mail.com")
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)
}
}