2187 lines
80 KiB
Go
2187 lines
80 KiB
Go
package sa
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"database/sql"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/big"
|
|
"net"
|
|
"os"
|
|
"reflect"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"golang.org/x/net/context"
|
|
|
|
"github.com/jmhodges/clock"
|
|
gorp "gopkg.in/go-gorp/gorp.v2"
|
|
jose "gopkg.in/square/go-jose.v2"
|
|
|
|
"github.com/letsencrypt/boulder/core"
|
|
corepb "github.com/letsencrypt/boulder/core/proto"
|
|
berrors "github.com/letsencrypt/boulder/errors"
|
|
blog "github.com/letsencrypt/boulder/log"
|
|
"github.com/letsencrypt/boulder/metrics"
|
|
"github.com/letsencrypt/boulder/revocation"
|
|
sapb "github.com/letsencrypt/boulder/sa/proto"
|
|
"github.com/letsencrypt/boulder/sa/satest"
|
|
"github.com/letsencrypt/boulder/test"
|
|
"github.com/letsencrypt/boulder/test/vars"
|
|
)
|
|
|
|
var log = blog.UseMock()
|
|
var ctx = context.Background()
|
|
|
|
// initSA constructs a SQLStorageAuthority and a clean up function
|
|
// that should be defer'ed to the end of the test.
|
|
func initSA(t *testing.T) (*SQLStorageAuthority, clock.FakeClock, func()) {
|
|
dbMap, err := NewDbMap(vars.DBConnSA, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create dbMap: %s", err)
|
|
}
|
|
|
|
fc := clock.NewFake()
|
|
fc.Set(time.Date(2015, 3, 4, 5, 0, 0, 0, time.UTC))
|
|
|
|
sa, err := NewSQLStorageAuthority(dbMap, fc, log, metrics.NewNoopScope(), 1)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create SA: %s", err)
|
|
}
|
|
|
|
cleanUp := test.ResetSATestDatabase(t)
|
|
return sa, fc, cleanUp
|
|
}
|
|
|
|
var (
|
|
anotherKey = `{
|
|
"kty":"RSA",
|
|
"n": "vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw",
|
|
"e":"AQAB"
|
|
}`
|
|
)
|
|
|
|
func TestAddRegistration(t *testing.T) {
|
|
sa, clk, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
jwk := satest.GoodJWK()
|
|
|
|
contact := "mailto:foo@example.com"
|
|
contacts := &[]string{contact}
|
|
reg, err := sa.NewRegistration(ctx, 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(ctx, 0)
|
|
test.AssertError(t, err, "Registration object for ID 0 was returned")
|
|
|
|
dbReg, err := sa.GetRegistration(ctx, 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")
|
|
|
|
newReg := core.Registration{
|
|
ID: reg.ID,
|
|
Key: jwk,
|
|
Contact: &[]string{"test.com"},
|
|
InitialIP: net.ParseIP("72.72.72.72"),
|
|
Agreement: "yes",
|
|
}
|
|
err = sa.UpdateRegistration(ctx, newReg)
|
|
test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.ID))
|
|
dbReg, err = sa.GetRegistrationByKey(ctx, 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(ctx, &anotherJWK)
|
|
test.AssertError(t, err, "Registration object for invalid key was returned")
|
|
}
|
|
|
|
func TestNoSuchRegistrationErrors(t *testing.T) {
|
|
sa, _, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
_, err := sa.GetRegistration(ctx, 100)
|
|
if !berrors.Is(err, berrors.NotFound) {
|
|
t.Errorf("GetRegistration: expected a berrors.NotFound type error, got %T type error (%s)", err, err)
|
|
}
|
|
|
|
jwk := satest.GoodJWK()
|
|
_, err = sa.GetRegistrationByKey(ctx, jwk)
|
|
if !berrors.Is(err, berrors.NotFound) {
|
|
t.Errorf("GetRegistrationByKey: expected a berrors.NotFound type error, got %T type error (%s)", err, err)
|
|
}
|
|
|
|
err = sa.UpdateRegistration(ctx, core.Registration{ID: 100, Key: jwk})
|
|
if !berrors.Is(err, berrors.NotFound) {
|
|
t.Errorf("UpdateRegistration: expected a berrors.NotFound type error, got %T type error (%v)", err, err)
|
|
}
|
|
}
|
|
|
|
func TestCountPendingAuthorizations(t *testing.T) {
|
|
sa, fc, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
expires := fc.Now().Add(time.Hour)
|
|
pendingAuthz := core.Authorization{
|
|
RegistrationID: reg.ID,
|
|
Expires: &expires,
|
|
}
|
|
|
|
pendingAuthz, err := sa.NewPendingAuthorization(ctx, pendingAuthz)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
count, err := sa.CountPendingAuthorizations(ctx, reg.ID)
|
|
test.AssertNotError(t, err, "Couldn't count pending authorizations")
|
|
test.AssertEquals(t, count, 0)
|
|
|
|
pendingAuthz.Status = core.StatusPending
|
|
pendingAuthz, err = sa.NewPendingAuthorization(ctx, pendingAuthz)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
count, err = sa.CountPendingAuthorizations(ctx, reg.ID)
|
|
test.AssertNotError(t, err, "Couldn't count pending authorizations")
|
|
test.AssertEquals(t, count, 1)
|
|
|
|
fc.Add(2 * time.Hour)
|
|
count, err = sa.CountPendingAuthorizations(ctx, reg.ID)
|
|
test.AssertNotError(t, err, "Couldn't count pending authorizations")
|
|
test.AssertEquals(t, count, 0)
|
|
}
|
|
|
|
func TestAddAuthorization(t *testing.T) {
|
|
sa, _, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
PA := core.Authorization{RegistrationID: reg.ID}
|
|
|
|
PA, err := sa.NewPendingAuthorization(ctx, PA)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
test.Assert(t, PA.ID != "", "ID shouldn't be blank")
|
|
|
|
dbPa, err := sa.GetAuthorization(ctx, PA.ID)
|
|
test.AssertNotError(t, err, "Couldn't get pending authorization with ID "+PA.ID)
|
|
test.AssertMarshaledEquals(t, PA, dbPa)
|
|
|
|
expectedPa := core.Authorization{ID: PA.ID}
|
|
test.AssertMarshaledEquals(t, dbPa.ID, expectedPa.ID)
|
|
|
|
combos := make([][]int, 1)
|
|
combos[0] = []int{0, 1}
|
|
|
|
exp := time.Now().AddDate(0, 0, 1)
|
|
identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "wut.com"}
|
|
newPa := core.Authorization{ID: PA.ID, Identifier: identifier, RegistrationID: reg.ID, Status: core.StatusPending, Expires: &exp, Combinations: combos}
|
|
|
|
newPa.Status = core.StatusValid
|
|
err = sa.FinalizeAuthorization(ctx, newPa)
|
|
test.AssertNotError(t, err, "Couldn't finalize pending authorization with ID "+PA.ID)
|
|
|
|
dbPa, err = sa.GetAuthorization(ctx, PA.ID)
|
|
test.AssertNotError(t, err, "Couldn't get authorization with ID "+PA.ID)
|
|
}
|
|
|
|
func TestRecyclePendingDisabled(t *testing.T) {
|
|
sa, _, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
pendingAuthz, err := sa.NewPendingAuthorization(ctx, core.Authorization{RegistrationID: reg.ID})
|
|
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
test.Assert(t, pendingAuthz.ID != "", "ID shouldn't be blank")
|
|
|
|
pendingAuthz2, err := sa.NewPendingAuthorization(ctx, core.Authorization{RegistrationID: reg.ID})
|
|
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
test.AssertNotEquals(t, pendingAuthz.ID, pendingAuthz2.ID)
|
|
}
|
|
|
|
func TestRecyclePendingEnabled(t *testing.T) {
|
|
|
|
sa, fc, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
expires := fc.Now()
|
|
authz := core.Authorization{
|
|
RegistrationID: reg.ID,
|
|
Identifier: core.AcmeIdentifier{
|
|
Type: "dns",
|
|
Value: "example.letsencrypt.org",
|
|
},
|
|
Challenges: []core.Challenge{
|
|
core.Challenge{
|
|
URI: "https://acme-example.letsencrypt.org/challenge123",
|
|
Type: "http-01",
|
|
Status: "pending",
|
|
Token: "abc",
|
|
},
|
|
},
|
|
Expires: &expires,
|
|
}
|
|
|
|
// Add expired authz
|
|
_, err := sa.NewPendingAuthorization(ctx, authz)
|
|
test.AssertNotError(t, err, "Couldn't create new expired pending authorization")
|
|
|
|
// Add expected authz
|
|
fc.Add(3 * time.Hour)
|
|
expires = fc.Now().Add(2 * time.Hour) // magic pointer
|
|
pendingAuthzA, err := sa.NewPendingAuthorization(ctx, authz)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
test.Assert(t, pendingAuthzA.ID != "", "ID shouldn't be blank")
|
|
// Add extra authz for kicks
|
|
pendingAuthzB, err := sa.NewPendingAuthorization(ctx, authz)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
test.Assert(t, pendingAuthzB.ID != "", "ID shouldn't be blank")
|
|
}
|
|
|
|
func CreateDomainAuth(t *testing.T, domainName string, sa *SQLStorageAuthority) (authz core.Authorization) {
|
|
return CreateDomainAuthWithRegID(t, domainName, sa, 42)
|
|
}
|
|
|
|
func CreateDomainAuthWithRegID(t *testing.T, domainName string, sa *SQLStorageAuthority, regID int64) (authz core.Authorization) {
|
|
exp := sa.clk.Now().AddDate(0, 0, 1) // expire in 1 day
|
|
|
|
combos := make([][]int, 1)
|
|
combos[0] = []int{0, 1}
|
|
|
|
// create pending auth
|
|
authz, err := sa.NewPendingAuthorization(ctx, core.Authorization{
|
|
Status: core.StatusPending,
|
|
Expires: &exp,
|
|
Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: domainName},
|
|
RegistrationID: regID,
|
|
Challenges: []core.Challenge{{}},
|
|
Combinations: combos,
|
|
})
|
|
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
|
|
chall := core.Challenge{Type: "simpleHttp", Status: core.StatusValid, URI: domainName, Token: "THISWOULDNTBEAGOODTOKEN"}
|
|
// Add some challenges
|
|
authz.Challenges = []core.Challenge{chall}
|
|
err = sa.UpdatePendingAuthorization(ctx, authz)
|
|
test.AssertNotError(t, err, "Couldn't update pending authorization with ID "+authz.ID)
|
|
|
|
return
|
|
}
|
|
|
|
// Ensure we get only valid authorization with correct RegID
|
|
func TestGetValidAuthorizationsBasic(t *testing.T) {
|
|
sa, clk, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
// Attempt to get unauthorized domain.
|
|
authzMap, err := sa.GetValidAuthorizations(ctx, 0, []string{"example.org"}, clk.Now())
|
|
// Should get no results, but not error.
|
|
test.AssertNotError(t, err, "Error getting valid authorizations")
|
|
test.AssertEquals(t, len(authzMap), 0)
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
|
|
// authorize "example.org"
|
|
authz := CreateDomainAuthWithRegID(t, "example.org", sa, reg.ID)
|
|
|
|
// finalize auth
|
|
authz.Status = core.StatusValid
|
|
err = sa.FinalizeAuthorization(ctx, authz)
|
|
test.AssertNotError(t, err, "Couldn't finalize pending authorization with ID "+authz.ID)
|
|
|
|
// attempt to get authorized domain with wrong RegID
|
|
authzMap, err = sa.GetValidAuthorizations(ctx, 0, []string{"example.org"}, clk.Now())
|
|
test.AssertNotError(t, err, "Error getting valid authorizations")
|
|
test.AssertEquals(t, len(authzMap), 0)
|
|
|
|
// get authorized domain
|
|
authzMap, err = sa.GetValidAuthorizations(ctx, reg.ID, []string{"example.org"}, clk.Now())
|
|
test.AssertNotError(t, err, "Should have found a valid auth for example.org and regID 42")
|
|
test.AssertEquals(t, len(authzMap), 1)
|
|
result := authzMap["example.org"]
|
|
test.AssertEquals(t, result.Status, core.StatusValid)
|
|
test.AssertEquals(t, result.Identifier.Type, core.IdentifierDNS)
|
|
test.AssertEquals(t, result.Identifier.Value, "example.org")
|
|
test.AssertEquals(t, result.RegistrationID, reg.ID)
|
|
}
|
|
|
|
func TestCountInvalidAuthorizations(t *testing.T) {
|
|
sa, _, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
|
|
key2 := new(jose.JSONWebKey)
|
|
key2.Key = &rsa.PublicKey{N: big.NewInt(1), E: 3}
|
|
reg2, err := sa.NewRegistration(context.Background(), core.Registration{
|
|
Key: key2,
|
|
InitialIP: net.ParseIP("88.77.66.11"),
|
|
CreatedAt: time.Date(2003, 5, 10, 0, 0, 0, 0, time.UTC),
|
|
Status: core.StatusValid,
|
|
})
|
|
test.AssertNotError(t, err, "making registration")
|
|
|
|
baseTime := time.Date(2017, 3, 4, 5, 0, 0, 0, time.UTC)
|
|
latest := baseTime.Add(3 * time.Hour)
|
|
|
|
makeInvalidAuthz := func(regID int64, domain string, offset time.Duration) {
|
|
authz := CreateDomainAuthWithRegID(t, domain, sa, regID)
|
|
exp := baseTime.Add(offset)
|
|
authz.Expires = &exp
|
|
authz.Status = "invalid"
|
|
err := sa.FinalizeAuthorization(ctx, authz)
|
|
test.AssertNotError(t, err, "Couldn't finalize pending authorization with ID "+authz.ID)
|
|
}
|
|
|
|
// We're going to count authzs for reg.ID and example.net, expiring between
|
|
// baseTime and baseTime + 3 hours, so add two examples that should be counted
|
|
// (1 hour from now and 2 hours from now), plus three that shouldn't be
|
|
// counted (too far future, wrong domain name, and wrong ID).
|
|
hostname := "example.net"
|
|
makeInvalidAuthz(reg.ID, hostname, time.Hour)
|
|
makeInvalidAuthz(reg.ID, hostname, 2*time.Hour)
|
|
makeInvalidAuthz(reg.ID, hostname, 24*time.Hour)
|
|
makeInvalidAuthz(reg.ID, "example.com", time.Hour)
|
|
makeInvalidAuthz(reg2.ID, hostname, time.Hour)
|
|
|
|
earliestNanos := baseTime.UnixNano()
|
|
latestNanos := latest.UnixNano()
|
|
|
|
count, err := sa.CountInvalidAuthorizations(context.Background(), &sapb.CountInvalidAuthorizationsRequest{
|
|
RegistrationID: ®.ID,
|
|
Hostname: &hostname,
|
|
Range: &sapb.Range{
|
|
Earliest: &earliestNanos,
|
|
Latest: &latestNanos,
|
|
},
|
|
})
|
|
test.AssertNotError(t, err, "counting invalid authorizations")
|
|
|
|
if *count.Count != 2 {
|
|
t.Errorf("expected to count 2 invalid authorizations, counted %d instead", *count.Count)
|
|
}
|
|
}
|
|
|
|
// Ensure we get the latest valid authorization for an ident
|
|
func TestGetValidAuthorizationsDuplicate(t *testing.T) {
|
|
sa, clk, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
domain := "example.org"
|
|
var err error
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
|
|
makeAuthz := func(daysToExpiry int, status core.AcmeStatus) core.Authorization {
|
|
authz := CreateDomainAuthWithRegID(t, domain, sa, reg.ID)
|
|
exp := clk.Now().AddDate(0, 0, daysToExpiry)
|
|
authz.Expires = &exp
|
|
authz.Status = status
|
|
err = sa.FinalizeAuthorization(ctx, authz)
|
|
test.AssertNotError(t, err, "Couldn't finalize pending authorization with ID "+authz.ID)
|
|
return authz
|
|
}
|
|
|
|
// create invalid authz
|
|
makeAuthz(10, core.StatusInvalid)
|
|
|
|
// should not get the auth
|
|
authzMap, err := sa.GetValidAuthorizations(ctx, reg.ID, []string{domain}, clk.Now())
|
|
test.AssertEquals(t, len(authzMap), 0)
|
|
|
|
// create valid auth
|
|
makeAuthz(1, core.StatusValid)
|
|
|
|
// should get the valid auth even if it's expire date is lower than the invalid one
|
|
authzMap, err = sa.GetValidAuthorizations(ctx, reg.ID, []string{domain}, clk.Now())
|
|
test.AssertNotError(t, err, "Should have found a valid auth for "+domain)
|
|
test.AssertEquals(t, len(authzMap), 1)
|
|
result1 := authzMap[domain]
|
|
test.AssertEquals(t, result1.Status, core.StatusValid)
|
|
test.AssertEquals(t, result1.Identifier.Type, core.IdentifierDNS)
|
|
test.AssertEquals(t, result1.Identifier.Value, domain)
|
|
test.AssertEquals(t, result1.RegistrationID, reg.ID)
|
|
|
|
// create a newer auth
|
|
newAuthz := makeAuthz(2, core.StatusValid)
|
|
|
|
authzMap, err = sa.GetValidAuthorizations(ctx, reg.ID, []string{domain}, clk.Now())
|
|
test.AssertNotError(t, err, "Should have found a valid auth for "+domain)
|
|
test.AssertEquals(t, len(authzMap), 1)
|
|
result2 := authzMap[domain]
|
|
test.AssertEquals(t, result2.Status, core.StatusValid)
|
|
test.AssertEquals(t, result2.Identifier.Type, core.IdentifierDNS)
|
|
test.AssertEquals(t, result2.Identifier.Value, domain)
|
|
test.AssertEquals(t, result2.RegistrationID, reg.ID)
|
|
// make sure we got the latest auth
|
|
test.AssertEquals(t, result2.ID, newAuthz.ID)
|
|
}
|
|
|
|
// Fetch multiple authzs at once. Check that
|
|
func TestGetValidAuthorizationsMultiple(t *testing.T) {
|
|
sa, clk, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
var err error
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
|
|
makeAuthz := func(daysToExpiry int, status core.AcmeStatus, domain string) core.Authorization {
|
|
authz := CreateDomainAuthWithRegID(t, domain, sa, reg.ID)
|
|
exp := clk.Now().AddDate(0, 0, daysToExpiry)
|
|
authz.Expires = &exp
|
|
authz.Status = status
|
|
err = sa.FinalizeAuthorization(ctx, authz)
|
|
test.AssertNotError(t, err, "Couldn't finalize pending authorization with ID "+authz.ID)
|
|
return authz
|
|
}
|
|
makeAuthz(1, core.StatusValid, "blog.example.com")
|
|
makeAuthz(2, core.StatusInvalid, "blog.example.com")
|
|
makeAuthz(5, core.StatusValid, "www.example.com")
|
|
wwwAuthz := makeAuthz(6, core.StatusValid, "www.example.com")
|
|
|
|
authzMap, err := sa.GetValidAuthorizations(ctx, reg.ID,
|
|
[]string{"blog.example.com", "www.example.com", "absent.example.com"}, clk.Now())
|
|
test.AssertNotError(t, err, "Couldn't get authorizations")
|
|
test.AssertEquals(t, len(authzMap), 2)
|
|
blogResult := authzMap["blog.example.com"]
|
|
if blogResult == nil {
|
|
t.Errorf("Didn't find blog.example.com in result")
|
|
}
|
|
if blogResult.Status == core.StatusInvalid {
|
|
t.Errorf("Got invalid blogResult")
|
|
}
|
|
wwwResult := authzMap["www.example.com"]
|
|
if wwwResult == nil {
|
|
t.Errorf("Didn't find www.example.com in result")
|
|
}
|
|
test.AssertEquals(t, wwwResult.ID, wwwAuthz.ID)
|
|
}
|
|
|
|
func TestAddCertificate(t *testing.T) {
|
|
sa, clk, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
|
|
// An example cert taken from EFF's website
|
|
certDER, err := ioutil.ReadFile("www.eff.org.der")
|
|
test.AssertNotError(t, err, "Couldn't read example cert DER")
|
|
|
|
digest, err := sa.AddCertificate(ctx, certDER, reg.ID, nil)
|
|
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
|
|
test.AssertEquals(t, digest, "qWoItDZmR4P9eFbeYgXXP3SR4ApnkQj8x4LsB_ORKBo")
|
|
|
|
retrievedCert, err := sa.GetCertificate(ctx, "000000000000000000000000000000021bd4")
|
|
test.AssertNotError(t, err, "Couldn't get www.eff.org.der by full serial")
|
|
test.AssertByteEquals(t, certDER, retrievedCert.DER)
|
|
|
|
certificateStatus, err := sa.GetCertificateStatus(ctx, "000000000000000000000000000000021bd4")
|
|
test.AssertNotError(t, err, "Couldn't get status for www.eff.org.der")
|
|
test.Assert(t, certificateStatus.Status == core.OCSPStatusGood, "OCSP Status should be good")
|
|
test.Assert(t, certificateStatus.OCSPLastUpdated.IsZero(), "OCSPLastUpdated should be nil")
|
|
test.AssertEquals(t, certificateStatus.NotAfter, retrievedCert.Expires)
|
|
|
|
// Test cert generated locally by Boulder / CFSSL, names [example.com,
|
|
// www.example.com, admin.example.com]
|
|
certDER2, err := ioutil.ReadFile("test-cert.der")
|
|
test.AssertNotError(t, err, "Couldn't read example cert DER")
|
|
serial := "ffdd9b8a82126d96f61d378d5ba99a0474f0"
|
|
|
|
digest2, err := sa.AddCertificate(ctx, certDER2, reg.ID, nil)
|
|
test.AssertNotError(t, err, "Couldn't add test-cert.der")
|
|
test.AssertEquals(t, digest2, "vrlPN5wIPME1D2PPsCy-fGnTWh8dMyyYQcXPRkjHAQI")
|
|
|
|
retrievedCert2, err := sa.GetCertificate(ctx, serial)
|
|
test.AssertNotError(t, err, "Couldn't get test-cert.der")
|
|
test.AssertByteEquals(t, certDER2, retrievedCert2.DER)
|
|
|
|
certificateStatus2, err := sa.GetCertificateStatus(ctx, serial)
|
|
test.AssertNotError(t, err, "Couldn't get status for test-cert.der")
|
|
test.Assert(t, certificateStatus2.Status == core.OCSPStatusGood, "OCSP Status should be good")
|
|
test.Assert(t, certificateStatus2.OCSPLastUpdated.IsZero(), "OCSPLastUpdated should be nil")
|
|
|
|
// Test adding OCSP response with cert
|
|
certDER3, err := ioutil.ReadFile("test-cert2.der")
|
|
test.AssertNotError(t, err, "Couldn't read example cert DER")
|
|
serial = "ffa0160630d618b2eb5c0510824b14274856"
|
|
ocspResp := []byte{0, 0, 1}
|
|
_, err = sa.AddCertificate(ctx, certDER3, reg.ID, ocspResp)
|
|
test.AssertNotError(t, err, "Couldn't add test-cert2.der")
|
|
|
|
certificateStatus3, err := sa.GetCertificateStatus(ctx, serial)
|
|
test.AssertNotError(t, err, "Couldn't get status for test-cert2.der")
|
|
test.Assert(
|
|
t,
|
|
bytes.Compare(certificateStatus3.OCSPResponse, ocspResp) == 0,
|
|
fmt.Sprintf("OCSP responses don't match, expected: %x, got %x", certificateStatus3.OCSPResponse, ocspResp),
|
|
)
|
|
test.Assert(
|
|
t,
|
|
clk.Now().Equal(certificateStatus3.OCSPLastUpdated),
|
|
fmt.Sprintf("OCSPLastUpdated doesn't match, expected %s, got %s", clk.Now(), certificateStatus3.OCSPLastUpdated),
|
|
)
|
|
}
|
|
|
|
func TestCountCertificatesByNames(t *testing.T) {
|
|
sa, clk, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
// Test cert generated locally by Boulder / CFSSL, names [example.com,
|
|
// www.example.com, admin.example.com]
|
|
certDER, err := ioutil.ReadFile("test-cert.der")
|
|
test.AssertNotError(t, err, "Couldn't read example cert DER")
|
|
|
|
cert, err := x509.ParseCertificate(certDER)
|
|
test.AssertNotError(t, err, "Couldn't parse example cert DER")
|
|
|
|
// Set the test clock's time to the time from the test certificate
|
|
clk.Add(-clk.Now().Sub(cert.NotBefore))
|
|
now := clk.Now()
|
|
yesterday := clk.Now().Add(-24 * time.Hour)
|
|
twoDaysAgo := clk.Now().Add(-48 * time.Hour)
|
|
tomorrow := clk.Now().Add(24 * time.Hour)
|
|
|
|
// Count for a name that doesn't have any certs
|
|
counts, err := sa.CountCertificatesByNames(ctx, []string{"example.com"}, yesterday, now)
|
|
test.AssertNotError(t, err, "Error counting certs.")
|
|
test.AssertEquals(t, len(counts), 1)
|
|
test.AssertEquals(t, *counts[0].Name, "example.com")
|
|
test.AssertEquals(t, *counts[0].Count, int64(0))
|
|
|
|
// Add the test cert and query for its names.
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
_, err = sa.AddCertificate(ctx, certDER, reg.ID, nil)
|
|
test.AssertNotError(t, err, "Couldn't add test-cert.der")
|
|
|
|
// Time range including now should find the cert
|
|
counts, err = sa.CountCertificatesByNames(ctx, []string{"example.com"}, yesterday, now)
|
|
test.AssertEquals(t, len(counts), 1)
|
|
test.AssertEquals(t, *counts[0].Name, "example.com")
|
|
test.AssertEquals(t, *counts[0].Count, int64(1))
|
|
|
|
// Time range between two days ago and yesterday should not.
|
|
counts, err = sa.CountCertificatesByNames(ctx, []string{"example.com"}, twoDaysAgo, yesterday)
|
|
test.AssertNotError(t, err, "Error counting certs.")
|
|
test.AssertEquals(t, len(counts), 1)
|
|
test.AssertEquals(t, *counts[0].Name, "example.com")
|
|
test.AssertEquals(t, *counts[0].Count, int64(0))
|
|
|
|
// Time range between now and tomorrow also should not (time ranges are
|
|
// inclusive at the tail end, but not the beginning end).
|
|
counts, err = sa.CountCertificatesByNames(ctx, []string{"example.com"}, now, tomorrow)
|
|
test.AssertNotError(t, err, "Error counting certs.")
|
|
test.AssertEquals(t, len(counts), 1)
|
|
test.AssertEquals(t, *counts[0].Name, "example.com")
|
|
test.AssertEquals(t, *counts[0].Count, int64(0))
|
|
|
|
// Add a second test cert (for example.co.bn) and query for multiple names.
|
|
names := []string{"example.com", "foo.com", "example.co.bn"}
|
|
|
|
// Override countCertificatesByName with an implementation of certCountFunc
|
|
// that will block forever if it's called in serial, but will succeed if
|
|
// called in parallel.
|
|
var interlocker sync.WaitGroup
|
|
interlocker.Add(len(names))
|
|
sa.parallelismPerRPC = len(names)
|
|
oldCertCountFunc := sa.countCertificatesByName
|
|
sa.countCertificatesByName = func(domain string, earliest, latest time.Time) (int, error) {
|
|
interlocker.Done()
|
|
interlocker.Wait()
|
|
return oldCertCountFunc(domain, earliest, latest)
|
|
}
|
|
|
|
certDER2, err := ioutil.ReadFile("test-cert2.der")
|
|
test.AssertNotError(t, err, "Couldn't read test-cert2.der")
|
|
_, err = sa.AddCertificate(ctx, certDER2, reg.ID, nil)
|
|
test.AssertNotError(t, err, "Couldn't add test-cert2.der")
|
|
counts, err = sa.CountCertificatesByNames(ctx, names, yesterday, now.Add(10000*time.Hour))
|
|
test.AssertNotError(t, err, "Error counting certs.")
|
|
test.AssertEquals(t, len(counts), 3)
|
|
|
|
expected := map[string]int{
|
|
"example.co.bn": 1,
|
|
"foo.com": 0,
|
|
"example.com": 1,
|
|
}
|
|
for _, entry := range counts {
|
|
domain := *entry.Name
|
|
actualCount := *entry.Count
|
|
expectedCount := int64(expected[domain])
|
|
test.AssertEquals(t, actualCount, expectedCount)
|
|
}
|
|
}
|
|
|
|
const (
|
|
sctVersion = 0
|
|
sctTimestamp = 1435787268907
|
|
sctLogID = "aPaY+B9kgr46jO65KB1M/HFRXWeT1ETRCmesu09P+8Q="
|
|
sctSignature = "BAMASDBGAiEA/4kz9wQq3NhvZ6VlOmjq2Z9MVHGrUjF8uxUG9n1uRc4CIQD2FYnnszKXrR9AP5kBWmTgh3fXy+VlHK8HZXfbzdFf7g=="
|
|
sctCertSerial = "ff000000000000012607e11a78ac01f9"
|
|
)
|
|
|
|
func TestAddSCTReceipt(t *testing.T) {
|
|
sigBytes, err := base64.StdEncoding.DecodeString(sctSignature)
|
|
test.AssertNotError(t, err, "Failed to decode SCT signature")
|
|
sct := core.SignedCertificateTimestamp{
|
|
SCTVersion: sctVersion,
|
|
LogID: sctLogID,
|
|
Timestamp: sctTimestamp,
|
|
Signature: sigBytes,
|
|
CertificateSerial: sctCertSerial,
|
|
}
|
|
sa, _, cleanup := initSA(t)
|
|
defer cleanup()
|
|
err = sa.AddSCTReceipt(ctx, sct)
|
|
test.AssertNotError(t, err, "Failed to add SCT receipt")
|
|
// Append only and unique on signature and across LogID and CertificateSerial
|
|
err = sa.AddSCTReceipt(ctx, sct)
|
|
test.AssertNotError(t, err, "Incorrectly returned error on duplicate SCT receipt")
|
|
}
|
|
|
|
func TestGetSCTReceipt(t *testing.T) {
|
|
sigBytes, err := base64.StdEncoding.DecodeString(sctSignature)
|
|
test.AssertNotError(t, err, "Failed to decode SCT signature")
|
|
sct := core.SignedCertificateTimestamp{
|
|
SCTVersion: sctVersion,
|
|
LogID: sctLogID,
|
|
Timestamp: sctTimestamp,
|
|
Signature: sigBytes,
|
|
CertificateSerial: sctCertSerial,
|
|
}
|
|
sa, _, cleanup := initSA(t)
|
|
defer cleanup()
|
|
err = sa.AddSCTReceipt(ctx, sct)
|
|
test.AssertNotError(t, err, "Failed to add SCT receipt")
|
|
|
|
sqlSCT, err := sa.GetSCTReceipt(ctx, sctCertSerial, sctLogID)
|
|
test.AssertNotError(t, err, "Failed to get existing SCT receipt")
|
|
test.Assert(t, sqlSCT.SCTVersion == sct.SCTVersion, "Invalid SCT version")
|
|
test.Assert(t, sqlSCT.LogID == sct.LogID, "Invalid log ID")
|
|
test.Assert(t, sqlSCT.Timestamp == sct.Timestamp, "Invalid timestamp")
|
|
test.Assert(t, bytes.Compare(sqlSCT.Signature, sct.Signature) == 0, "Invalid signature")
|
|
test.Assert(t, sqlSCT.CertificateSerial == sct.CertificateSerial, "Invalid certificate serial")
|
|
}
|
|
|
|
func TestMarkCertificateRevoked(t *testing.T) {
|
|
sa, fc, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
// Add a cert to the DB to test with.
|
|
certDER, err := ioutil.ReadFile("www.eff.org.der")
|
|
test.AssertNotError(t, err, "Couldn't read example cert DER")
|
|
_, err = sa.AddCertificate(ctx, certDER, reg.ID, nil)
|
|
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
|
|
|
|
serial := "000000000000000000000000000000021bd4"
|
|
const ocspResponse = "this is a fake OCSP response"
|
|
|
|
certificateStatusObj, err := sa.GetCertificateStatus(ctx, serial)
|
|
test.AssertEquals(t, certificateStatusObj.Status, core.OCSPStatusGood)
|
|
|
|
fc.Add(1 * time.Hour)
|
|
|
|
err = sa.MarkCertificateRevoked(ctx, serial, revocation.KeyCompromise)
|
|
test.AssertNotError(t, err, "MarkCertificateRevoked failed")
|
|
|
|
certificateStatusObj, err = sa.GetCertificateStatus(ctx, serial)
|
|
test.AssertNotError(t, err, "Failed to fetch certificate status")
|
|
|
|
if revocation.KeyCompromise != certificateStatusObj.RevokedReason {
|
|
t.Errorf("RevokedReasons, expected %v, got %v", revocation.KeyCompromise, certificateStatusObj.RevokedReason)
|
|
}
|
|
if !fc.Now().Equal(certificateStatusObj.RevokedDate) {
|
|
t.Errorf("RevokedData, expected %s, got %s", fc.Now(), certificateStatusObj.RevokedDate)
|
|
}
|
|
}
|
|
|
|
func TestCountCertificates(t *testing.T) {
|
|
sa, fc, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
fc.Add(time.Hour * 24)
|
|
now := fc.Now()
|
|
count, err := sa.CountCertificatesRange(ctx, now.Add(-24*time.Hour), now)
|
|
test.AssertNotError(t, err, "Couldn't get certificate count for the last 24hrs")
|
|
test.AssertEquals(t, count, int64(0))
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
// Add a cert to the DB to test with.
|
|
certDER, err := ioutil.ReadFile("www.eff.org.der")
|
|
test.AssertNotError(t, err, "Couldn't read example cert DER")
|
|
_, err = sa.AddCertificate(ctx, certDER, reg.ID, nil)
|
|
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
|
|
|
|
fc.Add(2 * time.Hour)
|
|
now = fc.Now()
|
|
count, err = sa.CountCertificatesRange(ctx, now.Add(-24*time.Hour), now)
|
|
test.AssertNotError(t, err, "Couldn't get certificate count for the last 24hrs")
|
|
test.AssertEquals(t, count, int64(1))
|
|
|
|
fc.Add(24 * time.Hour)
|
|
now = fc.Now()
|
|
count, err = sa.CountCertificatesRange(ctx, now.Add(-24*time.Hour), now)
|
|
test.AssertNotError(t, err, "Couldn't get certificate count for the last 24hrs")
|
|
test.AssertEquals(t, count, int64(0))
|
|
}
|
|
|
|
func TestCountRegistrationsByIP(t *testing.T) {
|
|
sa, fc, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
contact := "mailto:foo@example.com"
|
|
|
|
// Create one IPv4 registration
|
|
_, err := sa.NewRegistration(ctx, core.Registration{
|
|
Key: &jose.JSONWebKey{Key: &rsa.PublicKey{N: big.NewInt(1), E: 1}},
|
|
Contact: &[]string{contact},
|
|
InitialIP: net.ParseIP("43.34.43.34"),
|
|
})
|
|
// Create two IPv6 registrations, both within the same /48
|
|
test.AssertNotError(t, err, "Couldn't insert registration")
|
|
_, err = sa.NewRegistration(ctx, core.Registration{
|
|
Key: &jose.JSONWebKey{Key: &rsa.PublicKey{N: big.NewInt(2), E: 1}},
|
|
Contact: &[]string{contact},
|
|
InitialIP: net.ParseIP("2001:cdba:1234:5678:9101:1121:3257:9652"),
|
|
})
|
|
test.AssertNotError(t, err, "Couldn't insert registration")
|
|
_, err = sa.NewRegistration(ctx, core.Registration{
|
|
Key: &jose.JSONWebKey{Key: &rsa.PublicKey{N: big.NewInt(3), E: 1}},
|
|
Contact: &[]string{contact},
|
|
InitialIP: net.ParseIP("2001:cdba:1234:5678:9101:1121:3257:9653"),
|
|
})
|
|
test.AssertNotError(t, err, "Couldn't insert registration")
|
|
|
|
earliest := fc.Now().Add(-time.Hour * 24)
|
|
latest := fc.Now()
|
|
|
|
// There should be 0 registrations for an IPv4 address we didn't add
|
|
// a registration for
|
|
count, err := sa.CountRegistrationsByIP(ctx, net.ParseIP("1.1.1.1"), earliest, latest)
|
|
test.AssertNotError(t, err, "Failed to count registrations")
|
|
test.AssertEquals(t, count, 0)
|
|
// There should be 1 registration for the IPv4 address we did add
|
|
// a registration for
|
|
count, err = sa.CountRegistrationsByIP(ctx, net.ParseIP("43.34.43.34"), earliest, latest)
|
|
test.AssertNotError(t, err, "Failed to count registrations")
|
|
test.AssertEquals(t, count, 1)
|
|
// There should be 1 registration for the first IPv6 address we added
|
|
// a registration for
|
|
count, err = sa.CountRegistrationsByIP(ctx, net.ParseIP("2001:cdba:1234:5678:9101:1121:3257:9652"), earliest, latest)
|
|
test.AssertNotError(t, err, "Failed to count registrations")
|
|
test.AssertEquals(t, count, 1)
|
|
// There should be 1 registration for the second IPv6 address we added
|
|
// a registration for as well
|
|
count, err = sa.CountRegistrationsByIP(ctx, net.ParseIP("2001:cdba:1234:5678:9101:1121:3257:9653"), earliest, latest)
|
|
test.AssertNotError(t, err, "Failed to count registrations")
|
|
test.AssertEquals(t, count, 1)
|
|
// There should be 0 registrations for an IPv6 address in the same /48 as the
|
|
// two IPv6 addresses with registrations
|
|
count, err = sa.CountRegistrationsByIP(ctx, net.ParseIP("2001:cdba:1234:0000:0000:0000:0000:0000"), earliest, latest)
|
|
test.AssertNotError(t, err, "Failed to count registrations")
|
|
test.AssertEquals(t, count, 0)
|
|
}
|
|
|
|
func TestCountRegistrationsByIPRange(t *testing.T) {
|
|
sa, fc, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
contact := "mailto:foo@example.com"
|
|
|
|
// Create one IPv4 registration
|
|
_, err := sa.NewRegistration(ctx, core.Registration{
|
|
Key: &jose.JSONWebKey{Key: &rsa.PublicKey{N: big.NewInt(1), E: 1}},
|
|
Contact: &[]string{contact},
|
|
InitialIP: net.ParseIP("43.34.43.34"),
|
|
})
|
|
// Create two IPv6 registrations, both within the same /48
|
|
test.AssertNotError(t, err, "Couldn't insert registration")
|
|
_, err = sa.NewRegistration(ctx, core.Registration{
|
|
Key: &jose.JSONWebKey{Key: &rsa.PublicKey{N: big.NewInt(2), E: 1}},
|
|
Contact: &[]string{contact},
|
|
InitialIP: net.ParseIP("2001:cdba:1234:5678:9101:1121:3257:9652"),
|
|
})
|
|
test.AssertNotError(t, err, "Couldn't insert registration")
|
|
_, err = sa.NewRegistration(ctx, core.Registration{
|
|
Key: &jose.JSONWebKey{Key: &rsa.PublicKey{N: big.NewInt(3), E: 1}},
|
|
Contact: &[]string{contact},
|
|
InitialIP: net.ParseIP("2001:cdba:1234:5678:9101:1121:3257:9653"),
|
|
})
|
|
test.AssertNotError(t, err, "Couldn't insert registration")
|
|
|
|
earliest := fc.Now().Add(-time.Hour * 24)
|
|
latest := fc.Now()
|
|
|
|
// There should be 0 registrations in the range for an IPv4 address we didn't
|
|
// add a registration for
|
|
count, err := sa.CountRegistrationsByIPRange(ctx, net.ParseIP("1.1.1.1"), earliest, latest)
|
|
test.AssertNotError(t, err, "Failed to count registrations")
|
|
test.AssertEquals(t, count, 0)
|
|
// There should be 1 registration in the range for the IPv4 address we did
|
|
// add a registration for
|
|
count, err = sa.CountRegistrationsByIPRange(ctx, net.ParseIP("43.34.43.34"), earliest, latest)
|
|
test.AssertNotError(t, err, "Failed to count registrations")
|
|
test.AssertEquals(t, count, 1)
|
|
// There should be 2 registrations in the range for the first IPv6 address we added
|
|
// a registration for because it's in the same /48
|
|
count, err = sa.CountRegistrationsByIPRange(ctx, net.ParseIP("2001:cdba:1234:5678:9101:1121:3257:9652"), earliest, latest)
|
|
test.AssertNotError(t, err, "Failed to count registrations")
|
|
test.AssertEquals(t, count, 2)
|
|
// There should be 2 registrations in the range for the second IPv6 address
|
|
// we added a registration for as well, because it too is in the same /48
|
|
count, err = sa.CountRegistrationsByIPRange(ctx, net.ParseIP("2001:cdba:1234:5678:9101:1121:3257:9653"), earliest, latest)
|
|
test.AssertNotError(t, err, "Failed to count registrations")
|
|
test.AssertEquals(t, count, 2)
|
|
// There should also be 2 registrations in the range for an arbitrary IPv6 address in
|
|
// the same /48 as the registrations we added
|
|
count, err = sa.CountRegistrationsByIPRange(ctx, net.ParseIP("2001:cdba:1234:0000:0000:0000:0000:0000"), earliest, latest)
|
|
test.AssertNotError(t, err, "Failed to count registrations")
|
|
test.AssertEquals(t, count, 2)
|
|
}
|
|
|
|
func TestRevokeAuthorizationsByDomain(t *testing.T) {
|
|
sa, _, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
PA1 := CreateDomainAuthWithRegID(t, "a.com", sa, reg.ID)
|
|
PA2 := CreateDomainAuthWithRegID(t, "a.com", sa, reg.ID)
|
|
|
|
PA2.Status = core.StatusValid
|
|
err := sa.FinalizeAuthorization(ctx, PA2)
|
|
test.AssertNotError(t, err, "Failed to finalize authorization")
|
|
|
|
ident := core.AcmeIdentifier{Value: "a.com", Type: core.IdentifierDNS}
|
|
ar, par, err := sa.RevokeAuthorizationsByDomain(ctx, ident)
|
|
test.AssertNotError(t, err, "Failed to revoke authorizations for a.com")
|
|
test.AssertEquals(t, ar, int64(1))
|
|
test.AssertEquals(t, par, int64(1))
|
|
|
|
PA, err := sa.GetAuthorization(ctx, PA1.ID)
|
|
test.AssertNotError(t, err, "Failed to retrieve pending authorization")
|
|
FA, err := sa.GetAuthorization(ctx, PA2.ID)
|
|
test.AssertNotError(t, err, "Failed to retrieve finalized authorization")
|
|
|
|
test.AssertEquals(t, PA.Status, core.StatusRevoked)
|
|
test.AssertEquals(t, FA.Status, core.StatusRevoked)
|
|
}
|
|
|
|
func TestFQDNSets(t *testing.T) {
|
|
sa, fc, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
tx, err := sa.dbMap.Begin()
|
|
test.AssertNotError(t, err, "Failed to open transaction")
|
|
names := []string{"a.example.com", "B.example.com"}
|
|
expires := fc.Now().Add(time.Hour * 2).UTC()
|
|
issued := fc.Now()
|
|
err = addFQDNSet(tx, names, "serial", issued, expires)
|
|
test.AssertNotError(t, err, "Failed to add name set")
|
|
test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
|
|
|
|
// only one valid
|
|
threeHours := time.Hour * 3
|
|
count, err := sa.CountFQDNSets(ctx, threeHours, names)
|
|
test.AssertNotError(t, err, "Failed to count name sets")
|
|
test.AssertEquals(t, count, int64(1))
|
|
|
|
// check hash isn't affected by changing name order/casing
|
|
count, err = sa.CountFQDNSets(ctx, threeHours, []string{"b.example.com", "A.example.COM"})
|
|
test.AssertNotError(t, err, "Failed to count name sets")
|
|
test.AssertEquals(t, count, int64(1))
|
|
|
|
// add another valid set
|
|
tx, err = sa.dbMap.Begin()
|
|
test.AssertNotError(t, err, "Failed to open transaction")
|
|
err = addFQDNSet(tx, names, "anotherSerial", issued, expires)
|
|
test.AssertNotError(t, err, "Failed to add name set")
|
|
test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
|
|
|
|
// only two valid
|
|
count, err = sa.CountFQDNSets(ctx, threeHours, names)
|
|
test.AssertNotError(t, err, "Failed to count name sets")
|
|
test.AssertEquals(t, count, int64(2))
|
|
|
|
// add an expired set
|
|
tx, err = sa.dbMap.Begin()
|
|
test.AssertNotError(t, err, "Failed to open transaction")
|
|
err = addFQDNSet(
|
|
tx,
|
|
names,
|
|
"yetAnotherSerial",
|
|
issued.Add(-threeHours),
|
|
expires.Add(-threeHours),
|
|
)
|
|
test.AssertNotError(t, err, "Failed to add name set")
|
|
test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
|
|
|
|
// only two valid
|
|
count, err = sa.CountFQDNSets(ctx, threeHours, names)
|
|
test.AssertNotError(t, err, "Failed to count name sets")
|
|
test.AssertEquals(t, count, int64(2))
|
|
}
|
|
|
|
func TestFQDNSetsExists(t *testing.T) {
|
|
sa, fc, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
names := []string{"a.example.com", "B.example.com"}
|
|
exists, err := sa.FQDNSetExists(ctx, names)
|
|
test.AssertNotError(t, err, "Failed to check FQDN set existence")
|
|
test.Assert(t, !exists, "FQDN set shouldn't exist")
|
|
|
|
tx, err := sa.dbMap.Begin()
|
|
test.AssertNotError(t, err, "Failed to open transaction")
|
|
expires := fc.Now().Add(time.Hour * 2).UTC()
|
|
issued := fc.Now()
|
|
err = addFQDNSet(tx, names, "serial", issued, expires)
|
|
test.AssertNotError(t, err, "Failed to add name set")
|
|
test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
|
|
|
|
exists, err = sa.FQDNSetExists(ctx, names)
|
|
test.AssertNotError(t, err, "Failed to check FQDN set existence")
|
|
test.Assert(t, exists, "FQDN set does exist")
|
|
}
|
|
|
|
type execRecorder struct {
|
|
query string
|
|
args []interface{}
|
|
}
|
|
|
|
func (e *execRecorder) Exec(query string, args ...interface{}) (sql.Result, error) {
|
|
e.query = query
|
|
e.args = args
|
|
return nil, nil
|
|
}
|
|
|
|
func TestAddIssuedNames(t *testing.T) {
|
|
var e execRecorder
|
|
err := addIssuedNames(&e, &x509.Certificate{
|
|
DNSNames: []string{
|
|
"example.co.uk",
|
|
"example.xyz",
|
|
},
|
|
SerialNumber: big.NewInt(1),
|
|
NotBefore: time.Date(2015, 3, 4, 5, 0, 0, 0, time.UTC),
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := "INSERT INTO issuedNames (reversedName, serial, notBefore) VALUES (?, ?, ?), (?, ?, ?);"
|
|
if e.query != expected {
|
|
t.Errorf("Wrong query: got %q, expected %q", e.query, expected)
|
|
}
|
|
expectedArgs := []interface{}{
|
|
"uk.co.example",
|
|
"000000000000000000000000000000000001",
|
|
time.Date(2015, 3, 4, 5, 0, 0, 0, time.UTC),
|
|
"xyz.example",
|
|
"000000000000000000000000000000000001",
|
|
time.Date(2015, 3, 4, 5, 0, 0, 0, time.UTC),
|
|
}
|
|
if !reflect.DeepEqual(e.args, expectedArgs) {
|
|
t.Errorf("Wrong args: got\n%#v, expected\n%#v", e.args, expectedArgs)
|
|
}
|
|
}
|
|
|
|
func TestPreviousCertificateExists(t *testing.T) {
|
|
sa, _, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
|
|
// An example cert taken from EFF's website
|
|
certDER, err := ioutil.ReadFile("www.eff.org.der")
|
|
test.AssertNotError(t, err, "reading cert DER")
|
|
|
|
_, err = sa.AddCertificate(ctx, certDER, reg.ID, nil)
|
|
test.AssertNotError(t, err, "calling AddCertificate")
|
|
|
|
cases := []struct {
|
|
name string
|
|
domain string
|
|
regID int64
|
|
expected bool
|
|
}{
|
|
{"matches", "www.eff.org", reg.ID, true},
|
|
{"wrongDomain", "wwoof.org", reg.ID, false},
|
|
{"wrongAccount", "www.eff.org", 3333, false},
|
|
}
|
|
|
|
for _, testCase := range cases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
exists, err := sa.PreviousCertificateExists(context.Background(),
|
|
&sapb.PreviousCertificateExistsRequest{
|
|
Domain: &testCase.domain,
|
|
RegID: &testCase.regID,
|
|
})
|
|
test.AssertNotError(t, err, "calling PreviousCertificateExists")
|
|
if *exists.Exists != testCase.expected {
|
|
t.Errorf("wanted %v got %v", testCase.expected, *exists.Exists)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeactivateAuthorization(t *testing.T) {
|
|
sa, _, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
PA := core.Authorization{RegistrationID: reg.ID}
|
|
|
|
PA, err := sa.NewPendingAuthorization(ctx, PA)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
test.Assert(t, PA.ID != "", "ID shouldn't be blank")
|
|
|
|
dbPa, err := sa.GetAuthorization(ctx, PA.ID)
|
|
test.AssertNotError(t, err, "Couldn't get pending authorization with ID "+PA.ID)
|
|
test.AssertMarshaledEquals(t, PA, dbPa)
|
|
|
|
expectedPa := core.Authorization{ID: PA.ID}
|
|
test.AssertMarshaledEquals(t, dbPa.ID, expectedPa.ID)
|
|
|
|
combos := make([][]int, 1)
|
|
combos[0] = []int{0, 1}
|
|
|
|
exp := time.Now().AddDate(0, 0, 1)
|
|
identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "wut.com"}
|
|
newPa := core.Authorization{
|
|
ID: PA.ID,
|
|
Identifier: identifier,
|
|
RegistrationID: reg.ID,
|
|
Status: core.StatusPending,
|
|
Expires: &exp,
|
|
Combinations: combos,
|
|
}
|
|
|
|
newPa.Status = core.StatusValid
|
|
err = sa.FinalizeAuthorization(ctx, newPa)
|
|
test.AssertNotError(t, err, "Couldn't finalize pending authorization with ID "+PA.ID)
|
|
|
|
dbPa, err = sa.GetAuthorization(ctx, PA.ID)
|
|
test.AssertNotError(t, err, "Couldn't get authorization with ID "+PA.ID)
|
|
|
|
err = sa.DeactivateAuthorization(ctx, dbPa.ID)
|
|
test.AssertNotError(t, err, "Couldn't deactivate valid authorization with ID "+PA.ID)
|
|
|
|
dbPa, err = sa.GetAuthorization(ctx, PA.ID)
|
|
test.AssertNotError(t, err, "Couldn't get authorization with ID "+PA.ID)
|
|
test.AssertEquals(t, dbPa.Status, core.StatusDeactivated)
|
|
|
|
PA.Status = core.StatusPending
|
|
PA, err = sa.NewPendingAuthorization(ctx, PA)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
test.Assert(t, PA.ID != "", "ID shouldn't be blank")
|
|
|
|
err = sa.DeactivateAuthorization(ctx, PA.ID)
|
|
test.AssertNotError(t, err, "Couldn't deactivate pending authorization with ID "+PA.ID)
|
|
|
|
dbPa, err = sa.GetAuthorization(ctx, PA.ID)
|
|
test.AssertNotError(t, err, "Couldn't get authorization with ID "+PA.ID)
|
|
test.AssertEquals(t, dbPa.Status, core.StatusDeactivated)
|
|
|
|
pendingObj, err := sa.dbMap.Get(&pendingauthzModel{}, PA.ID)
|
|
test.AssertNotError(t, err, "sa.dbMap.Get failed to get pending authz")
|
|
test.Assert(t, pendingObj == nil, "Deactivated authorization still in pending table")
|
|
}
|
|
|
|
func TestDeactivateAccount(t *testing.T) {
|
|
sa, _, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
|
|
err := sa.DeactivateRegistration(context.Background(), reg.ID)
|
|
test.AssertNotError(t, err, "DeactivateRegistration failed")
|
|
|
|
dbReg, err := sa.GetRegistration(context.Background(), reg.ID)
|
|
test.AssertNotError(t, err, "GetRegistration failed")
|
|
test.AssertEquals(t, dbReg.Status, core.StatusDeactivated)
|
|
}
|
|
|
|
func TestReverseName(t *testing.T) {
|
|
testCases := []struct {
|
|
inputDomain string
|
|
inputReversed string
|
|
}{
|
|
{"", ""},
|
|
{"...", "..."},
|
|
{"com", "com"},
|
|
{"example.com", "com.example"},
|
|
{"www.example.com", "com.example.www"},
|
|
{"world.wide.web.example.com", "com.example.web.wide.world"},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
output := ReverseName(tc.inputDomain)
|
|
test.AssertEquals(t, output, tc.inputReversed)
|
|
}
|
|
}
|
|
|
|
type fqdnTestcase struct {
|
|
Serial string
|
|
Names []string
|
|
ExpectedHash setHash
|
|
Issued time.Time
|
|
Expires time.Time
|
|
}
|
|
|
|
func setupFQDNSets(t *testing.T, db *gorp.DbMap, fc clock.FakeClock) map[string]fqdnTestcase {
|
|
namesA := []string{"a.example.com", "B.example.com"}
|
|
namesB := []string{"example.org"}
|
|
namesC := []string{"letsencrypt.org"}
|
|
expectedHashA := setHash{0x92, 0xc7, 0xf2, 0x47, 0xbd, 0x1e, 0xea, 0x8d, 0x52, 0x7f, 0xb0, 0x59, 0x19, 0xe9, 0xbe, 0x81, 0x78, 0x88, 0xe6, 0xf7, 0x55, 0xf0, 0x1c, 0xc9, 0x63, 0x15, 0x5f, 0x8e, 0x52, 0xae, 0x95, 0xc1}
|
|
expectedHashB := setHash{0xbf, 0xab, 0xc3, 0x74, 0x32, 0x95, 0x8b, 0x6, 0x33, 0x60, 0xd3, 0xad, 0x64, 0x61, 0xc9, 0xc4, 0x73, 0x5a, 0xe7, 0xf8, 0xed, 0xd4, 0x65, 0x92, 0xa5, 0xe0, 0xf0, 0x14, 0x52, 0xb2, 0xe4, 0xb5}
|
|
expectedHashC := setHash{0xf2, 0xbb, 0x7b, 0xab, 0x8, 0x2c, 0x18, 0xee, 0x8, 0x97, 0x17, 0xbe, 0x67, 0xd7, 0x12, 0x14, 0xaa, 0x4, 0xac, 0xe2, 0x29, 0x2a, 0x67, 0x2c, 0x37, 0x2c, 0xf3, 0x33, 0xe1, 0xb0, 0xd8, 0xe7}
|
|
|
|
now := fc.Now()
|
|
|
|
testcases := map[string]fqdnTestcase{
|
|
// One test case with serial "a" issued now and expiring in two hours for
|
|
// namesA
|
|
"a": fqdnTestcase{
|
|
Serial: "a",
|
|
Names: namesA,
|
|
ExpectedHash: expectedHashA,
|
|
Issued: now,
|
|
Expires: now.Add(time.Hour * 2).UTC(),
|
|
},
|
|
// One test case with serial "b", issued one hour from now and expiring in
|
|
// two hours, also for namesA
|
|
"b": fqdnTestcase{
|
|
Serial: "b",
|
|
Names: namesA,
|
|
ExpectedHash: expectedHashA,
|
|
Issued: now.Add(time.Hour),
|
|
Expires: now.Add(time.Hour * 2).UTC(),
|
|
},
|
|
// One test case with serial "c", issued one hour from now and expiring in
|
|
// two hours, for namesB
|
|
"c": fqdnTestcase{
|
|
Serial: "c",
|
|
Names: namesB,
|
|
ExpectedHash: expectedHashB,
|
|
Issued: now.Add(time.Hour),
|
|
Expires: now.Add(time.Hour * 2).UTC(),
|
|
},
|
|
// One test case with serial "d", issued five hours in the past and expiring
|
|
// in two hours from now, with namesC
|
|
"d": fqdnTestcase{
|
|
Serial: "d",
|
|
Names: namesC,
|
|
ExpectedHash: expectedHashC,
|
|
Issued: now.Add(-5 * time.Hour),
|
|
Expires: now.Add(time.Hour * 2).UTC(),
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
tx, err := db.Begin()
|
|
test.AssertNotError(t, err, "Failed to open transaction")
|
|
err = addFQDNSet(tx, tc.Names, tc.Serial, tc.Issued, tc.Expires)
|
|
test.AssertNotError(t, err, fmt.Sprintf("Failed to add fqdnSet for %#v", tc))
|
|
test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
|
|
}
|
|
|
|
return testcases
|
|
}
|
|
|
|
func TestGetFQDNSetsBySerials(t *testing.T) {
|
|
sa, fc, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
// Add the test fqdn sets
|
|
testcases := setupFQDNSets(t, sa.dbMap, fc)
|
|
|
|
// Asking for the fqdnSets for no serials should produce an error since this
|
|
// is not expected in normal conditions
|
|
fqdnSets, err := sa.getFQDNSetsBySerials([]string{})
|
|
test.AssertError(t, err, "No error calling getFQDNSetsBySerials for empty serials")
|
|
test.AssertEquals(t, len(fqdnSets), 0)
|
|
|
|
// Asking for the fqdnSets for serials that don't exist should return nothing
|
|
fqdnSets, err = sa.getFQDNSetsBySerials([]string{"this", "doesn't", "exist"})
|
|
test.AssertNotError(t, err, "Error calling getFQDNSetsBySerials for non-existent serials")
|
|
test.AssertEquals(t, len(fqdnSets), 0)
|
|
|
|
// Asking for the fqdnSets for serial "a" should return the expectedHashA hash
|
|
fqdnSets, err = sa.getFQDNSetsBySerials([]string{"a"})
|
|
test.AssertNotError(t, err, "Error calling getFQDNSetsBySerials for serial \"a\"")
|
|
test.AssertEquals(t, len(fqdnSets), 1)
|
|
test.AssertEquals(t, string(fqdnSets[0]), string(testcases["a"].ExpectedHash))
|
|
|
|
// Asking for the fqdnSets for serial "b" should return the expectedHashA hash
|
|
// because cert "b" has namesA subjects
|
|
fqdnSets, err = sa.getFQDNSetsBySerials([]string{"b"})
|
|
test.AssertNotError(t, err, "Error calling getFQDNSetsBySerials for serial \"b\"")
|
|
test.AssertEquals(t, len(fqdnSets), 1)
|
|
test.AssertEquals(t, string(fqdnSets[0]), string(testcases["b"].ExpectedHash))
|
|
|
|
// Asking for the fqdnSets for serial "d" should return the expectedHashC hash
|
|
// because cert "d" has namesC subjects
|
|
fqdnSets, err = sa.getFQDNSetsBySerials([]string{"d"})
|
|
test.AssertNotError(t, err, "Error calling getFQDNSetsBySerials for serial \"d\"")
|
|
test.AssertEquals(t, len(fqdnSets), 1)
|
|
test.AssertEquals(t, string(fqdnSets[0]), string(testcases["d"].ExpectedHash))
|
|
|
|
// Asking for the fqdnSets for serial "c" should return the expectedHashB hash
|
|
// because cert "c" has namesB subjects
|
|
fqdnSets, err = sa.getFQDNSetsBySerials([]string{"c"})
|
|
test.AssertNotError(t, err, "Error calling getFQDNSetsBySerials for serial \"c\"")
|
|
test.AssertEquals(t, len(fqdnSets), 1)
|
|
test.AssertEquals(t, string(fqdnSets[0]), string(testcases["c"].ExpectedHash))
|
|
|
|
// Asking for the fqdnSets for serial "a", "b", "c" and "made up" should return
|
|
// the three expected hashes - two expectedHashA (for "a" and "b"), one
|
|
// expectedHashB (for "c")
|
|
expectedHashes := map[string]int{
|
|
string(testcases["a"].ExpectedHash): 2,
|
|
string(testcases["c"].ExpectedHash): 1,
|
|
}
|
|
fqdnSets, err = sa.getFQDNSetsBySerials([]string{"a", "b", "c", "made up"})
|
|
test.AssertNotError(t, err, "Error calling getFQDNSetsBySerials for serial \"a\", \"b\", \"c\", \"made up\"")
|
|
|
|
for _, setHash := range fqdnSets {
|
|
setHashKey := string(setHash)
|
|
if _, present := expectedHashes[setHashKey]; !present {
|
|
t.Errorf("Unexpected setHash in results: %#v", setHash)
|
|
}
|
|
expectedHashes[setHashKey]--
|
|
if expectedHashes[setHashKey] <= 0 {
|
|
delete(expectedHashes, setHashKey)
|
|
}
|
|
}
|
|
if len(expectedHashes) != 0 {
|
|
t.Errorf("Some expected setHashes were not observed: %#v", expectedHashes)
|
|
}
|
|
}
|
|
|
|
func TestGetNewIssuancesByFQDNSet(t *testing.T) {
|
|
sa, fc, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
// Add the test fqdn sets
|
|
testcases := setupFQDNSets(t, sa.dbMap, fc)
|
|
|
|
// Use one hour ago as the earliest cut off
|
|
earliest := fc.Now().Add(-time.Hour)
|
|
|
|
// Calling getNewIssuancesByFQDNSet with an empty FQDNSet should error
|
|
count, err := sa.getNewIssuancesByFQDNSet(nil, earliest)
|
|
test.AssertError(t, err, "No error calling getNewIssuancesByFQDNSet for empty fqdn set")
|
|
test.AssertEquals(t, count, -1)
|
|
|
|
// Calling getNewIssuancesByFQDNSet with FQDNSet hashes that don't exist
|
|
// should return 0
|
|
count, err = sa.getNewIssuancesByFQDNSet([]setHash{setHash{0xC0, 0xFF, 0xEE}, setHash{0x13, 0x37}}, earliest)
|
|
test.AssertNotError(t, err, "Error calling getNewIssuancesByFQDNSet for non-existent set hashes")
|
|
test.AssertEquals(t, count, 0)
|
|
|
|
// Calling getNewIssuancesByFQDNSet with the "a" expected hash should return
|
|
// 1, since both testcase "b" was a renewal of testcase "a"
|
|
count, err = sa.getNewIssuancesByFQDNSet([]setHash{testcases["a"].ExpectedHash}, earliest)
|
|
test.AssertNotError(t, err, "Error calling getNewIssuancesByFQDNSet for testcase a")
|
|
test.AssertEquals(t, count, 1)
|
|
|
|
// Calling getNewIssuancesByFQDNSet with the "c" expected hash should return
|
|
// 1, since there is only one issuance for this sethash
|
|
count, err = sa.getNewIssuancesByFQDNSet([]setHash{testcases["c"].ExpectedHash}, earliest)
|
|
test.AssertNotError(t, err, "Error calling getNewIssuancesByFQDNSet for testcase c")
|
|
test.AssertEquals(t, count, 1)
|
|
|
|
// Calling getNewIssuancesByFQDNSet with the "c" and "d" expected hashes should return
|
|
// only 1, since there is only one issuance for the provided set hashes that
|
|
// is within the earliest window. The issuance for "d" was too far in the past
|
|
// to be counted
|
|
count, err = sa.getNewIssuancesByFQDNSet([]setHash{testcases["c"].ExpectedHash, testcases["d"].ExpectedHash}, earliest)
|
|
test.AssertNotError(t, err, "Error calling getNewIssuancesByFQDNSet for testcase c and d")
|
|
test.AssertEquals(t, count, 1)
|
|
|
|
// But by moving the earliest point behind the "d" issuance, we should now get a count of 2
|
|
count, err = sa.getNewIssuancesByFQDNSet([]setHash{testcases["c"].ExpectedHash, testcases["d"].ExpectedHash}, earliest.Add(-6*time.Hour))
|
|
test.AssertNotError(t, err, "Error calling getNewIssuancesByFQDNSet for testcase c and d with adjusted earliest")
|
|
test.AssertEquals(t, count, 2)
|
|
}
|
|
|
|
func TestNewOrder(t *testing.T) {
|
|
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
|
return
|
|
}
|
|
|
|
sa, _, cleanup := initSA(t)
|
|
defer cleanup()
|
|
|
|
// Create a test registration to reference
|
|
reg, err := sa.NewRegistration(ctx, core.Registration{
|
|
Key: &jose.JSONWebKey{Key: &rsa.PublicKey{N: big.NewInt(1), E: 1}},
|
|
InitialIP: net.ParseIP("42.42.42.42"),
|
|
})
|
|
test.AssertNotError(t, err, "Couldn't create test registration")
|
|
|
|
i := int64(1)
|
|
status := string(core.StatusPending)
|
|
order, err := sa.NewOrder(context.Background(), &corepb.Order{
|
|
RegistrationID: ®.ID,
|
|
Expires: &i,
|
|
Names: []string{"example.com", "just.another.example.com"},
|
|
Authorizations: []string{"a", "b", "c"},
|
|
Status: &status,
|
|
})
|
|
test.AssertNotError(t, err, "sa.NewOrder failed")
|
|
test.AssertEquals(t, *order.Id, int64(1))
|
|
|
|
var authzIDs []string
|
|
_, err = sa.dbMap.Select(&authzIDs, "SELECT authzID FROM orderToAuthz WHERE orderID = ?;", *order.Id)
|
|
test.AssertNotError(t, err, "Failed to count orderToAuthz entries")
|
|
test.AssertEquals(t, len(authzIDs), 3)
|
|
test.AssertDeepEquals(t, authzIDs, []string{"a", "b", "c"})
|
|
|
|
names, err := sa.namesForOrder(*order.Id)
|
|
test.AssertNotError(t, err, "namesForOrder errored")
|
|
test.AssertEquals(t, len(names), 2)
|
|
test.AssertDeepEquals(t, names, []string{"com.example", "com.example.another.just"})
|
|
}
|
|
|
|
func TestSetOrderProcessing(t *testing.T) {
|
|
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
|
return
|
|
}
|
|
|
|
sa, fc, cleanup := initSA(t)
|
|
defer cleanup()
|
|
|
|
// Create a test registration to reference
|
|
reg, err := sa.NewRegistration(ctx, core.Registration{
|
|
Key: &jose.JSONWebKey{Key: &rsa.PublicKey{N: big.NewInt(1), E: 1}},
|
|
InitialIP: net.ParseIP("42.42.42.42"),
|
|
})
|
|
test.AssertNotError(t, err, "Couldn't create test registration")
|
|
|
|
// Add one pending authz
|
|
authzExpires := fc.Now().Add(time.Hour)
|
|
newAuthz := core.Authorization{
|
|
Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "example.com"},
|
|
RegistrationID: reg.ID,
|
|
Status: core.StatusPending,
|
|
Expires: &authzExpires,
|
|
}
|
|
authz, err := sa.NewPendingAuthorization(ctx, newAuthz)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
|
|
// Update the pending authz to be valid
|
|
authz.Status = core.StatusValid
|
|
err = sa.FinalizeAuthorization(ctx, authz)
|
|
test.AssertNotError(t, err, "Couldn't finalize pending authz to valid")
|
|
|
|
i := int64(1337)
|
|
order := &corepb.Order{
|
|
RegistrationID: ®.ID,
|
|
Expires: &i,
|
|
Names: []string{"example.com"},
|
|
Authorizations: []string{authz.ID},
|
|
}
|
|
|
|
// Add a new order in pending status with no certificate serial
|
|
order, err = sa.NewOrder(context.Background(), order)
|
|
test.AssertNotError(t, err, "NewOrder failed")
|
|
|
|
// Set the order to be processing
|
|
err = sa.SetOrderProcessing(context.Background(), order)
|
|
test.AssertNotError(t, err, "SetOrderProcessing failed")
|
|
|
|
// Read the order by ID from the DB to check the status was correctly updated
|
|
// to processing
|
|
updatedOrder, err := sa.GetOrder(
|
|
context.Background(),
|
|
&sapb.OrderRequest{Id: order.Id})
|
|
test.AssertNotError(t, err, "GetOrder failed")
|
|
test.AssertEquals(t, *updatedOrder.Status, string(core.StatusProcessing))
|
|
test.AssertEquals(t, *updatedOrder.BeganProcessing, true)
|
|
}
|
|
|
|
func TestFinalizeOrder(t *testing.T) {
|
|
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
|
return
|
|
}
|
|
|
|
sa, fc, cleanup := initSA(t)
|
|
defer cleanup()
|
|
|
|
// Create a test registration to reference
|
|
reg, err := sa.NewRegistration(ctx, core.Registration{
|
|
Key: &jose.JSONWebKey{Key: &rsa.PublicKey{N: big.NewInt(1), E: 1}},
|
|
InitialIP: net.ParseIP("42.42.42.42"),
|
|
})
|
|
test.AssertNotError(t, err, "Couldn't create test registration")
|
|
|
|
// Add one pending authz
|
|
authzExpires := fc.Now().Add(time.Hour)
|
|
newAuthz := core.Authorization{
|
|
Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "example.com"},
|
|
RegistrationID: reg.ID,
|
|
Status: core.StatusPending,
|
|
Expires: &authzExpires,
|
|
}
|
|
authz, err := sa.NewPendingAuthorization(ctx, newAuthz)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
|
|
// Set the authz to valid
|
|
authz.Status = core.StatusValid
|
|
err = sa.FinalizeAuthorization(ctx, authz)
|
|
test.AssertNotError(t, err, "Couldn't finalize pending authorization")
|
|
|
|
i := int64(1337)
|
|
order := &corepb.Order{
|
|
RegistrationID: ®.ID,
|
|
Expires: &i,
|
|
Names: []string{"example.com"},
|
|
Authorizations: []string{authz.ID},
|
|
}
|
|
|
|
// Add a new order with an empty certificate serial
|
|
order, err = sa.NewOrder(context.Background(), order)
|
|
test.AssertNotError(t, err, "NewOrder failed")
|
|
|
|
// Set the order to processing so it can be finalized
|
|
err = sa.SetOrderProcessing(ctx, order)
|
|
test.AssertNotError(t, err, "SetOrderProcessing failed")
|
|
|
|
// Finalize the order with a certificate serial
|
|
serial := "eat.serial.for.breakfast"
|
|
order.CertificateSerial = &serial
|
|
err = sa.FinalizeOrder(context.Background(), order)
|
|
test.AssertNotError(t, err, "FinalizeOrder failed")
|
|
|
|
// Read the order by ID from the DB to check the certificate serial and status
|
|
// was correctly updated
|
|
updatedOrder, err := sa.GetOrder(
|
|
context.Background(),
|
|
&sapb.OrderRequest{Id: order.Id})
|
|
test.AssertNotError(t, err, "GetOrder failed")
|
|
test.AssertEquals(t, *updatedOrder.CertificateSerial, serial)
|
|
test.AssertEquals(t, *updatedOrder.Status, string(core.StatusValid))
|
|
}
|
|
|
|
func TestOrder(t *testing.T) {
|
|
// Only run under test/config-next config where 20170731115209_AddOrders.sql
|
|
// has been applied
|
|
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
|
return
|
|
}
|
|
|
|
sa, fc, cleanup := initSA(t)
|
|
defer cleanup()
|
|
|
|
// Create a test registration to reference
|
|
reg, err := sa.NewRegistration(ctx, core.Registration{
|
|
Key: &jose.JSONWebKey{Key: &rsa.PublicKey{N: big.NewInt(1), E: 1}},
|
|
InitialIP: net.ParseIP("42.42.42.42"),
|
|
})
|
|
test.AssertNotError(t, err, "Couldn't create test registration")
|
|
|
|
// Add one pending authz
|
|
authzExpires := fc.Now().Add(time.Hour)
|
|
newAuthz := core.Authorization{
|
|
Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "example.com"},
|
|
RegistrationID: reg.ID,
|
|
Status: core.StatusPending,
|
|
Expires: &authzExpires,
|
|
}
|
|
authz, err := sa.NewPendingAuthorization(ctx, newAuthz)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
|
|
expires := time.Now().Truncate(time.Second).UnixNano()
|
|
empty := ""
|
|
|
|
inputOrder := &corepb.Order{
|
|
RegistrationID: ®.ID,
|
|
Expires: &expires,
|
|
Names: []string{"example.com"},
|
|
Authorizations: []string{authz.ID},
|
|
}
|
|
|
|
order, err := sa.NewOrder(context.Background(), inputOrder)
|
|
test.AssertNotError(t, err, "sa.NewOrder failed")
|
|
test.AssertEquals(t, *order.Id, int64(1))
|
|
|
|
pendingStatus := string(core.StatusPending)
|
|
falseBool := false
|
|
one := int64(1)
|
|
expectedOrder := &corepb.Order{
|
|
// The expected order matches the input order
|
|
RegistrationID: inputOrder.RegistrationID,
|
|
Expires: inputOrder.Expires,
|
|
Names: inputOrder.Names,
|
|
Authorizations: inputOrder.Authorizations,
|
|
// And should also have an empty serial and the correct status and
|
|
// processing state, and an ID of 1
|
|
Id: &one,
|
|
CertificateSerial: &empty,
|
|
BeganProcessing: &falseBool,
|
|
Status: &pendingStatus,
|
|
}
|
|
|
|
storedOrder, err := sa.GetOrder(context.Background(), &sapb.OrderRequest{Id: order.Id})
|
|
test.AssertNotError(t, err, "sa.Order failed")
|
|
test.AssertDeepEquals(t, storedOrder, expectedOrder)
|
|
}
|
|
|
|
func TestGetValidOrderAuthorizations(t *testing.T) {
|
|
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
|
return
|
|
}
|
|
|
|
sa, _, cleanup := initSA(t)
|
|
defer cleanup()
|
|
|
|
// Create a throw away registration
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
|
|
// Create and finalize an authz for the throw-away reg and "example.com"
|
|
authz := CreateDomainAuthWithRegID(t, "example.com", sa, reg.ID)
|
|
exp := sa.clk.Now().Add(time.Hour * 24 * 7)
|
|
authz.Expires = &exp
|
|
authz.Status = "valid"
|
|
err := sa.FinalizeAuthorization(ctx, authz)
|
|
test.AssertNotError(t, err, "Couldn't create final authz with ID "+authz.ID)
|
|
|
|
// Now create a new order that references the above authorization
|
|
i := time.Now().Truncate(time.Second).UnixNano()
|
|
status := string(core.StatusPending)
|
|
order := &corepb.Order{
|
|
RegistrationID: ®.ID,
|
|
Expires: &i,
|
|
Names: []string{"example.com"},
|
|
Authorizations: []string{authz.ID},
|
|
Status: &status,
|
|
}
|
|
order, err = sa.NewOrder(context.Background(), order)
|
|
test.AssertNotError(t, err, "AddOrder failed")
|
|
|
|
// Now fetch the order authorizations for the order we added for the
|
|
// throw-away reg
|
|
authzMap, err := sa.GetValidOrderAuthorizations(
|
|
context.Background(),
|
|
&sapb.GetValidOrderAuthorizationsRequest{
|
|
Id: order.Id,
|
|
AcctID: ®.ID,
|
|
})
|
|
// It should not fail and one valid authorization for the example.com domain
|
|
// should be present with ID and status equal to the authz we created earlier.
|
|
test.AssertNotError(t, err, "GetValidOrderAuthorizations failed")
|
|
test.AssertNotNil(t, authzMap, "GetValidOrderAuthorizations result was nil")
|
|
test.AssertEquals(t, len(authzMap), 1)
|
|
test.AssertNotNil(t, authzMap["example.com"], "Authz for example.com was nil")
|
|
test.AssertEquals(t, authzMap["example.com"].ID, authz.ID)
|
|
test.AssertEquals(t, string(authzMap["example.com"].Status), "valid")
|
|
|
|
// Getting the order authorizations for an order that doesn't exist should return nothing
|
|
missingID := int64(0xC0FFEEEEEEE)
|
|
authzMap, err = sa.GetValidOrderAuthorizations(
|
|
context.Background(),
|
|
&sapb.GetValidOrderAuthorizationsRequest{
|
|
Id: &missingID,
|
|
AcctID: ®.ID,
|
|
})
|
|
test.AssertNotError(t, err, "GetValidOrderAuthorizations for non-existent order errored")
|
|
test.AssertEquals(t, len(authzMap), 0)
|
|
|
|
// Getting the order authorizations for an order that does exist, but for the
|
|
// wrong acct ID should return nothing
|
|
wrongAcctID := int64(0xDEADDA7ABA5E)
|
|
authzMap, err = sa.GetValidOrderAuthorizations(
|
|
context.Background(),
|
|
&sapb.GetValidOrderAuthorizationsRequest{
|
|
Id: order.Id,
|
|
AcctID: &wrongAcctID,
|
|
})
|
|
test.AssertNotError(t, err, "GetValidOrderAuthorizations for existent order, wrong acctID errored")
|
|
test.AssertEquals(t, len(authzMap), 0)
|
|
}
|
|
|
|
// TestGetAuthorizationNoRows ensures that the GetAuthorization function returns
|
|
// the correct error when there are no results for the provided ID.
|
|
func TestGetAuthorizationNoRows(t *testing.T) {
|
|
sa, _, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
// An empty authz ID should result in `sql.ErrNoRows`
|
|
_, err := sa.GetAuthorization(ctx, "")
|
|
test.AssertError(t, err, "Didn't get an error looking up empty authz ID")
|
|
test.Assert(t, berrors.Is(err, berrors.NotFound), "GetAuthorization did not return a berrors.NotFound error")
|
|
}
|
|
|
|
func TestGetAuthorizations(t *testing.T) {
|
|
sa, fc, cleanup := initSA(t)
|
|
defer cleanup()
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
exp := fc.Now().AddDate(0, 0, 1)
|
|
pa := core.Authorization{RegistrationID: reg.ID, Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "a"}, Status: core.StatusPending, Expires: &exp, Combinations: [][]int{[]int{0, 1}}}
|
|
|
|
paA, err := sa.NewPendingAuthorization(ctx, pa)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
test.Assert(t, paA.ID != "", "ID shouldn't be blank")
|
|
|
|
pa.Identifier.Value = "b"
|
|
paB, err := sa.NewPendingAuthorization(ctx, pa)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
test.Assert(t, paB.ID != "", "ID shouldn't be blank")
|
|
|
|
paB.Status = core.StatusValid
|
|
err = sa.FinalizeAuthorization(ctx, paB)
|
|
test.AssertNotError(t, err, "Couldn't finalize pending authorization with ID "+paB.ID)
|
|
|
|
requireV2Authzs := false
|
|
|
|
now := fc.Now().UnixNano()
|
|
authz, err := sa.GetAuthorizations(context.Background(), &sapb.GetAuthorizationsRequest{
|
|
RegistrationID: ®.ID,
|
|
Domains: []string{"a", "b"},
|
|
Now: &now,
|
|
RequireV2Authzs: &requireV2Authzs,
|
|
})
|
|
test.AssertNotError(t, err, "sa.GetAuthorizations failed")
|
|
test.AssertEquals(t, len(authz.Authz), 2)
|
|
authz, err = sa.GetAuthorizations(context.Background(), &sapb.GetAuthorizationsRequest{
|
|
RegistrationID: ®.ID,
|
|
Domains: []string{"a", "b", "c"},
|
|
Now: &now,
|
|
RequireV2Authzs: &requireV2Authzs,
|
|
})
|
|
test.AssertNotError(t, err, "sa.GetAuthorizations failed")
|
|
test.AssertEquals(t, len(authz.Authz), 2)
|
|
}
|
|
|
|
func TestAddPendingAuthorizations(t *testing.T) {
|
|
sa, fc, cleanup := initSA(t)
|
|
defer cleanup()
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
expires := fc.Now().Add(time.Hour).UnixNano()
|
|
identA := `a`
|
|
identB := `a`
|
|
combo := []byte(`[[0]]`)
|
|
status := string(core.StatusPending)
|
|
empty := ""
|
|
authz := []*corepb.Authorization{
|
|
&corepb.Authorization{
|
|
Id: &empty,
|
|
Identifier: &identA,
|
|
RegistrationID: ®.ID,
|
|
Status: &status,
|
|
Expires: &expires,
|
|
Combinations: combo,
|
|
},
|
|
&corepb.Authorization{
|
|
Id: &empty,
|
|
Identifier: &identB,
|
|
RegistrationID: ®.ID,
|
|
Status: &status,
|
|
Expires: &expires,
|
|
Combinations: combo,
|
|
},
|
|
}
|
|
|
|
ids, err := sa.AddPendingAuthorizations(context.Background(), &sapb.AddPendingAuthorizationsRequest{Authz: authz})
|
|
test.AssertNotError(t, err, "sa.AddPendingAuthorizations failed")
|
|
test.AssertEquals(t, len(ids.Ids), 2)
|
|
|
|
for _, id := range ids.Ids {
|
|
_, err := sa.GetAuthorization(context.Background(), id)
|
|
test.AssertNotError(t, err, "sa.GetAuthorization failed")
|
|
}
|
|
}
|
|
|
|
func TestCountPendingOrders(t *testing.T) {
|
|
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
|
return
|
|
}
|
|
|
|
sa, fc, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
authzExpires := fc.Now().Add(time.Hour)
|
|
expires := authzExpires.UnixNano()
|
|
|
|
// Counting pending orders for a reg ID that doesn't exist should return 0
|
|
count, err := sa.CountPendingOrders(ctx, 12345)
|
|
test.AssertNotError(t, err, "Couldn't count pending authorizations for fake reg ID")
|
|
test.AssertEquals(t, count, 0)
|
|
|
|
// Add one pending authz
|
|
newAuthz := core.Authorization{
|
|
Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "example.com"},
|
|
RegistrationID: reg.ID,
|
|
Status: core.StatusPending,
|
|
Expires: &authzExpires,
|
|
}
|
|
pendingAuthz, err := sa.NewPendingAuthorization(ctx, newAuthz)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
|
|
// Add one pending order
|
|
_, err = sa.NewOrder(ctx, &corepb.Order{
|
|
RegistrationID: ®.ID,
|
|
Expires: &expires,
|
|
Names: []string{"example.com"},
|
|
Authorizations: []string{pendingAuthz.ID},
|
|
})
|
|
test.AssertNotError(t, err, "Couldn't create new pending order")
|
|
|
|
// We expect there to be a count of one for this reg ID
|
|
count, err = sa.CountPendingOrders(ctx, reg.ID)
|
|
test.AssertNotError(t, err, "Couldn't count pending authorizations")
|
|
test.AssertEquals(t, count, 1)
|
|
|
|
// Create another fresh pending authz
|
|
secondPendingAuthz, err := sa.NewPendingAuthorization(ctx, newAuthz)
|
|
test.AssertNotError(t, err, "Couldn't create 2nd new pending authorization")
|
|
|
|
// Create a pending order that expired an hour ago
|
|
expires = fc.Now().Add(-time.Hour).UnixNano()
|
|
_, err = sa.NewOrder(ctx, &corepb.Order{
|
|
RegistrationID: ®.ID,
|
|
Expires: &expires,
|
|
Names: []string{"example.com"},
|
|
Authorizations: []string{secondPendingAuthz.ID},
|
|
})
|
|
test.AssertNotError(t, err, "Couldn't create new expired pending order")
|
|
|
|
// We still expect there to be a count of one for this reg ID since the order
|
|
// added above is expired
|
|
count, err = sa.CountPendingOrders(ctx, reg.ID)
|
|
test.AssertNotError(t, err, "Couldn't count pending authorizations")
|
|
test.AssertEquals(t, count, 1)
|
|
|
|
// Create another fresh pending authz
|
|
thirdPendingAuthz, err := sa.NewPendingAuthorization(ctx, newAuthz)
|
|
test.AssertNotError(t, err, "Couldn't create third new pending authorization")
|
|
// And then deactivate it
|
|
err = sa.DeactivateAuthorization(ctx, thirdPendingAuthz.ID)
|
|
test.AssertNotError(t, err, "Couldn't deactivate third new pending authorization")
|
|
|
|
// Create a deactivated order by referencing the deactivated authz
|
|
expires = fc.Now().Add(time.Hour).UnixNano()
|
|
_, err = sa.NewOrder(ctx, &corepb.Order{
|
|
RegistrationID: ®.ID,
|
|
Expires: &expires,
|
|
Names: []string{"example.com"},
|
|
Authorizations: []string{thirdPendingAuthz.ID},
|
|
})
|
|
test.AssertNotError(t, err, "Couldn't create new non-pending order")
|
|
|
|
// We still expect there to be a count of one for this reg ID since the order
|
|
// added above is not pending
|
|
count, err = sa.CountPendingOrders(ctx, reg.ID)
|
|
test.AssertNotError(t, err, "Couldn't count pending authorizations")
|
|
test.AssertEquals(t, count, 1)
|
|
|
|
// If the clock is advanced by two hours we expect the count to return to
|
|
// 0 for this reg ID since all of the pending orders we created will have
|
|
// expired.
|
|
fc.Add(2 * time.Hour)
|
|
count, err = sa.CountPendingOrders(ctx, reg.ID)
|
|
test.AssertNotError(t, err, "Couldn't count pending authorizations")
|
|
test.AssertEquals(t, count, 0)
|
|
}
|
|
|
|
func TestGetOrderForNames(t *testing.T) {
|
|
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
|
return
|
|
}
|
|
|
|
sa, fc, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
// Give the order we create a short lifetime
|
|
orderLifetime := time.Hour
|
|
expires := fc.Now().Add(orderLifetime).UnixNano()
|
|
|
|
// Create two test registrations to associate with orders
|
|
regA, err := sa.NewRegistration(ctx, core.Registration{
|
|
Key: satest.GoodJWK(),
|
|
InitialIP: net.ParseIP("42.42.42.42"),
|
|
})
|
|
test.AssertNotError(t, err, "Couldn't create test registration")
|
|
|
|
// Add one pending authz for the first name for regA
|
|
authzExpires := fc.Now().Add(time.Hour)
|
|
newAuthzA := core.Authorization{
|
|
Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "example.com"},
|
|
RegistrationID: regA.ID,
|
|
Status: core.StatusPending,
|
|
Expires: &authzExpires,
|
|
}
|
|
pendingAuthzA, err := sa.NewPendingAuthorization(ctx, newAuthzA)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization for regA")
|
|
|
|
// Add one pending authz for the second name for regA
|
|
newAuthzB := core.Authorization{
|
|
Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "just.another.example.com"},
|
|
RegistrationID: regA.ID,
|
|
Status: core.StatusPending,
|
|
Expires: &authzExpires,
|
|
}
|
|
pendingAuthzB, err := sa.NewPendingAuthorization(ctx, newAuthzB)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization for regA")
|
|
|
|
ctx := context.Background()
|
|
names := []string{"example.com", "just.another.example.com"}
|
|
|
|
// Call GetOrderForNames for a set of names we haven't created an order for
|
|
// yet
|
|
result, err := sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{
|
|
AcctID: ®A.ID,
|
|
Names: names,
|
|
})
|
|
// We expect the result to return an error
|
|
test.AssertError(t, err, "sa.GetOrderForNames did not return an error for an empty result")
|
|
// The error should be a notfound error
|
|
test.AssertEquals(t, berrors.Is(err, berrors.NotFound), true)
|
|
// The result should be nil
|
|
test.Assert(t, result == nil, "sa.GetOrderForNames for non-existent order returned non-nil result")
|
|
|
|
// Add a new order for a set of names
|
|
order, err := sa.NewOrder(ctx, &corepb.Order{
|
|
RegistrationID: ®A.ID,
|
|
Expires: &expires,
|
|
Authorizations: []string{pendingAuthzA.ID, pendingAuthzB.ID},
|
|
Names: names,
|
|
})
|
|
// It shouldn't error
|
|
test.AssertNotError(t, err, "sa.NewOrder failed")
|
|
// The order ID shouldn't be nil
|
|
test.AssertNotNil(t, *order.Id, "NewOrder returned with a nil Id")
|
|
|
|
// Call GetOrderForNames with the same account ID and set of names as the
|
|
// above NewOrder call
|
|
result, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{
|
|
AcctID: ®A.ID,
|
|
Names: names,
|
|
})
|
|
// It shouldn't error
|
|
test.AssertNotError(t, err, "sa.GetOrderForNames failed")
|
|
// The order returned should have the same ID as the order we created above
|
|
test.AssertNotNil(t, result, "Returned order was nil")
|
|
test.AssertEquals(t, *result.Id, *order.Id)
|
|
|
|
// Call GetOrderForNames with a different account ID from the NewOrder call
|
|
regB := int64(1337)
|
|
result, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{
|
|
AcctID: ®B,
|
|
Names: names,
|
|
})
|
|
// It should error
|
|
test.AssertError(t, err, "sa.GetOrderForNames did not return an error for an empty result")
|
|
// The error should be a notfound error
|
|
test.AssertEquals(t, berrors.Is(err, berrors.NotFound), true)
|
|
// The result should be nil
|
|
test.Assert(t, result == nil, "sa.GetOrderForNames for diff AcctID returned non-nil result")
|
|
|
|
// Advance the clock beyond the initial order's lifetime
|
|
fc.Add(2 * orderLifetime)
|
|
|
|
// Call GetOrderForNames again with the same account ID and set of names as
|
|
// the initial NewOrder call
|
|
result, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{
|
|
AcctID: ®A.ID,
|
|
Names: names,
|
|
})
|
|
// It should error since there is no result
|
|
test.AssertError(t, err, "sa.GetOrderForNames did not return an error for an empty result")
|
|
// The error should be a notfound error
|
|
test.AssertEquals(t, berrors.Is(err, berrors.NotFound), true)
|
|
// The result should be nil because the initial order expired & we don't want
|
|
// to return expired orders
|
|
test.Assert(t, result == nil, "sa.GetOrderForNames returned non-nil result for expired order case")
|
|
|
|
// Add a fresh order for a different of names.
|
|
expires = fc.Now().Add(orderLifetime).UnixNano()
|
|
names = []string{"zombo.com", "welcome.to.zombo.com"}
|
|
order, err = sa.NewOrder(ctx, &corepb.Order{
|
|
RegistrationID: ®A.ID,
|
|
Expires: &expires,
|
|
// We can use the same pending authz's as above - it doesn't matter that
|
|
// they are for different names for this test, we just need some authz IDs
|
|
// that exist.
|
|
Authorizations: []string{pendingAuthzA.ID, pendingAuthzB.ID},
|
|
Names: names,
|
|
})
|
|
// It shouldn't error
|
|
test.AssertNotError(t, err, "sa.NewOrder failed")
|
|
// The order ID shouldn't be nil
|
|
test.AssertNotNil(t, *order.Id, "NewOrder returned with a nil Id")
|
|
|
|
// Set the order processing so it can be finalized
|
|
err = sa.SetOrderProcessing(ctx, order)
|
|
test.AssertNotError(t, err, "sa.SetOrderProcessing failed")
|
|
|
|
// Finalize the order
|
|
serial := "cinnamon toast crunch"
|
|
order.CertificateSerial = &serial
|
|
err = sa.FinalizeOrder(ctx, order)
|
|
test.AssertNotError(t, err, "sa.FinalizeOrder failed")
|
|
|
|
// Call GetOrderForNames with the same account ID and set of names as
|
|
// the above NewOrder call
|
|
result, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{
|
|
AcctID: ®A.ID,
|
|
Names: names,
|
|
})
|
|
// It should error since there is no result
|
|
test.AssertError(t, err, "sa.GetOrderForNames did not return an error for an empty result")
|
|
// The error should be a notfound error
|
|
test.AssertEquals(t, berrors.Is(err, berrors.NotFound), true)
|
|
// The result should be nil because the one matching order has been finalized
|
|
// already
|
|
test.Assert(t, result == nil, "sa.GetOrderForNames returned non-nil result for finalized order case")
|
|
}
|
|
|
|
func TestUpdatePendingAuthorizationInvalidOrder(t *testing.T) {
|
|
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
|
return
|
|
}
|
|
|
|
sa, fc, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
expires := fc.Now().Add(time.Hour)
|
|
ctx := context.Background()
|
|
|
|
// Create a registration to work with
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
|
|
// Create a pending authz, not associated with any orders
|
|
authz := core.Authorization{
|
|
RegistrationID: reg.ID,
|
|
Expires: &expires,
|
|
Status: core.StatusPending,
|
|
Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "your.order.is.up"},
|
|
}
|
|
pendingAuthz, err := sa.NewPendingAuthorization(ctx, authz)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
|
|
// Update the pending authz to be invalid. This shouldn't error.
|
|
pendingAuthz.Status = core.StatusInvalid
|
|
err = sa.FinalizeAuthorization(ctx, pendingAuthz)
|
|
test.AssertNotError(t, err, "Couldn't finalize legacy pending authz to invalid")
|
|
|
|
// Create a pending authz that will be associated with an order
|
|
authz = core.Authorization{
|
|
RegistrationID: reg.ID,
|
|
Expires: &expires,
|
|
Status: core.StatusPending,
|
|
Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "your.order.is.up"},
|
|
}
|
|
pendingAuthz, err = sa.NewPendingAuthorization(ctx, authz)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
|
|
// Add a new order that references the above pending authz
|
|
expiresNano := expires.UnixNano()
|
|
order, err := sa.NewOrder(ctx, &corepb.Order{
|
|
RegistrationID: ®.ID,
|
|
Expires: &expiresNano,
|
|
Authorizations: []string{pendingAuthz.ID},
|
|
Names: []string{"your.order.is.up"},
|
|
})
|
|
// It shouldn't error
|
|
test.AssertNotError(t, err, "sa.NewOrder failed")
|
|
// The order ID shouldn't be nil
|
|
test.AssertNotNil(t, *order.Id, "NewOrder returned with a nil Id")
|
|
// The order should be pending
|
|
test.AssertEquals(t, *order.Status, string(core.StatusPending))
|
|
// The order should have one authz with the correct ID
|
|
test.AssertEquals(t, len(order.Authorizations), 1)
|
|
test.AssertEquals(t, order.Authorizations[0], pendingAuthz.ID)
|
|
|
|
// Now finalize the authz to an invalid status.
|
|
pendingAuthz.Status = core.StatusInvalid
|
|
err = sa.FinalizeAuthorization(ctx, pendingAuthz)
|
|
test.AssertNotError(t, err, "Couldn't finalize pending authz associated with order to invalid")
|
|
|
|
// Fetch the order to get its updated status
|
|
updatedOrder, err := sa.GetOrder(
|
|
context.Background(),
|
|
&sapb.OrderRequest{Id: order.Id})
|
|
test.AssertNotError(t, err, "GetOrder failed")
|
|
// We expect the updated order status to be invalid
|
|
test.AssertEquals(t, *updatedOrder.Status, string(core.StatusInvalid))
|
|
}
|
|
|
|
func TestStatusForOrder(t *testing.T) {
|
|
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
|
return
|
|
}
|
|
|
|
sa, fc, cleanUp := initSA(t)
|
|
defer cleanUp()
|
|
|
|
ctx := context.Background()
|
|
expires := fc.Now().Add(time.Hour)
|
|
expiresNano := expires.UnixNano()
|
|
alreadyExpired := expires.Add(-2 * time.Hour)
|
|
|
|
// Create a registration to work with
|
|
reg := satest.CreateWorkingRegistration(t, sa)
|
|
|
|
// Create a pending authz
|
|
newAuthz := core.Authorization{
|
|
RegistrationID: reg.ID,
|
|
Expires: &expires,
|
|
Status: core.StatusPending,
|
|
Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "pending.your.order.is.up"},
|
|
}
|
|
pendingAuthz, err := sa.NewPendingAuthorization(ctx, newAuthz)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
|
|
// Create an expired authz
|
|
newExpiredAuthz := core.Authorization{
|
|
RegistrationID: newAuthz.RegistrationID,
|
|
Expires: &alreadyExpired,
|
|
Status: newAuthz.Status,
|
|
Identifier: core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "expired.your.order.is.up"},
|
|
}
|
|
expiredAuthz, err := sa.NewPendingAuthorization(ctx, newExpiredAuthz)
|
|
test.AssertNotError(t, err, "Couldn't create new expired pending authorization")
|
|
|
|
// Create an invalid authz
|
|
invalidAuthz, err := sa.NewPendingAuthorization(ctx, newAuthz)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
invalidAuthz.Status = core.StatusInvalid
|
|
invalidAuthz.Identifier.Value = "invalid.your.order.is.up"
|
|
err = sa.FinalizeAuthorization(ctx, invalidAuthz)
|
|
test.AssertNotError(t, err, "Couldn't finalize pending authz to invalid")
|
|
|
|
// Create a deactivate authz
|
|
deactivatedAuthz, err := sa.NewPendingAuthorization(ctx, newAuthz)
|
|
test.AssertNotError(t, err, "Couldn't create new pending authorization")
|
|
deactivatedAuthz.Status = core.StatusDeactivated
|
|
deactivatedAuthz.Identifier.Value = "deactivated.your.order.is.up"
|
|
err = sa.FinalizeAuthorization(ctx, deactivatedAuthz)
|
|
test.AssertNotError(t, err, "Couldn't finalize pending authz to deactivated")
|
|
|
|
// Create a valid authz
|
|
validAuthz, err := sa.NewPendingAuthorization(ctx, newAuthz)
|
|
validAuthz.Status = core.StatusValid
|
|
validAuthz.Identifier.Value = "valid.your.order.is.up"
|
|
err = sa.FinalizeAuthorization(ctx, validAuthz)
|
|
test.AssertNotError(t, err, "Couldn't finalize pending authz to valid")
|
|
|
|
testCases := []struct {
|
|
Name string
|
|
AuthorizationIDs []string
|
|
OrderNames []string
|
|
ExpectedStatus string
|
|
SetProcessing bool
|
|
Finalize bool
|
|
}{
|
|
{
|
|
Name: "Order with an invalid authz",
|
|
OrderNames: []string{"pending.your.order.is.up", "invalid.your.order.is.up", "deactivated.your.order.is.up", "valid.your.order.is.up"},
|
|
AuthorizationIDs: []string{pendingAuthz.ID, invalidAuthz.ID, deactivatedAuthz.ID, validAuthz.ID},
|
|
ExpectedStatus: string(core.StatusInvalid),
|
|
},
|
|
{
|
|
Name: "Order with an expired authz",
|
|
OrderNames: []string{"pending.your.order.is.up", "expired.your.order.is.up", "deactivated.your.order.is.up", "valid.your.order.is.up"},
|
|
AuthorizationIDs: []string{pendingAuthz.ID, expiredAuthz.ID, deactivatedAuthz.ID, validAuthz.ID},
|
|
ExpectedStatus: string(core.StatusInvalid),
|
|
},
|
|
{
|
|
Name: "Order with a deactivated authz",
|
|
OrderNames: []string{"pending.your.order.is.up", "deactivated.your.order.is.up", "valid.your.order.is.up"},
|
|
AuthorizationIDs: []string{pendingAuthz.ID, deactivatedAuthz.ID, validAuthz.ID},
|
|
ExpectedStatus: string(core.StatusDeactivated),
|
|
},
|
|
{
|
|
Name: "Order with a pending authz",
|
|
OrderNames: []string{"valid.your.order.is.up", "pending.your.order.is.up"},
|
|
AuthorizationIDs: []string{validAuthz.ID, pendingAuthz.ID},
|
|
ExpectedStatus: string(core.StatusPending),
|
|
},
|
|
{
|
|
Name: "Order with only valid authzs, not yet processed or finalized",
|
|
OrderNames: []string{"valid.your.order.is.up"},
|
|
AuthorizationIDs: []string{validAuthz.ID},
|
|
ExpectedStatus: string(core.StatusPending),
|
|
},
|
|
{
|
|
Name: "Order with only valid authzs, set processing",
|
|
OrderNames: []string{"valid.your.order.is.up"},
|
|
AuthorizationIDs: []string{validAuthz.ID},
|
|
SetProcessing: true,
|
|
ExpectedStatus: string(core.StatusProcessing),
|
|
},
|
|
{
|
|
Name: "Order with only valid authzs, set processing and finalized",
|
|
OrderNames: []string{"valid.your.order.is.up"},
|
|
AuthorizationIDs: []string{validAuthz.ID},
|
|
SetProcessing: true,
|
|
Finalize: true,
|
|
ExpectedStatus: string(core.StatusValid),
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
// Add a new order with the testcase authz IDs
|
|
newOrder, err := sa.NewOrder(ctx, &corepb.Order{
|
|
RegistrationID: ®.ID,
|
|
Expires: &expiresNano,
|
|
Authorizations: tc.AuthorizationIDs,
|
|
Names: tc.OrderNames,
|
|
})
|
|
test.AssertNotError(t, err, "NewOrder errored unexpectedly")
|
|
// If requested, set the order to processing
|
|
if tc.SetProcessing {
|
|
err := sa.SetOrderProcessing(ctx, newOrder)
|
|
test.AssertNotError(t, err, "Error setting order to processing status")
|
|
}
|
|
// If requested, finalize the order
|
|
if tc.Finalize {
|
|
cereal := "lucky charms"
|
|
newOrder.CertificateSerial = &cereal
|
|
err := sa.FinalizeOrder(ctx, newOrder)
|
|
test.AssertNotError(t, err, "Error finalizing order")
|
|
}
|
|
// Fetch the order by ID to get its calculated status
|
|
storedOrder, err := sa.GetOrder(ctx, &sapb.OrderRequest{Id: newOrder.Id})
|
|
test.AssertNotError(t, err, "GetOrder failed")
|
|
// The status shouldn't be nil
|
|
test.AssertNotNil(t, storedOrder.Status, "Order status was nil")
|
|
// The status should match expected
|
|
test.AssertEquals(t, *storedOrder.Status, tc.ExpectedStatus)
|
|
})
|
|
}
|
|
|
|
}
|