Merge branch 'master' into clean_up_new_reg_test
This commit is contained in:
commit
845e1261a4
|
|
@ -18,7 +18,6 @@ import (
|
|||
blog "github.com/letsencrypt/boulder/log"
|
||||
"github.com/letsencrypt/boulder/ra"
|
||||
"github.com/letsencrypt/boulder/rpc"
|
||||
"github.com/letsencrypt/boulder/wfe"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
@ -44,7 +43,6 @@ func main() {
|
|||
cmd.FailOnError(err, "Couldn't create PA")
|
||||
|
||||
rai := ra.NewRegistrationAuthorityImpl(clock.Default(), auditlogger)
|
||||
rai.AuthzBase = c.Common.BaseURL + wfe.AuthzPath
|
||||
rai.PA = pa
|
||||
raDNSTimeout, err := time.ParseDuration(c.Common.DNSTimeout)
|
||||
cmd.FailOnError(err, "Couldn't parse RA DNS timeout")
|
||||
|
|
|
|||
|
|
@ -241,6 +241,8 @@ type ValidationRecord struct {
|
|||
// challenge, we just throw all the elements into one bucket,
|
||||
// together with the common metadata elements.
|
||||
type Challenge struct {
|
||||
ID int64 `json:"id,omitempty"`
|
||||
|
||||
// The type of challenge
|
||||
Type string `json:"type"`
|
||||
|
||||
|
|
@ -255,7 +257,7 @@ type Challenge struct {
|
|||
Validated *time.Time `json:"validated,omitempty"`
|
||||
|
||||
// A URI to which a response can be POSTed
|
||||
URI *AcmeURL `json:"uri"`
|
||||
URI string `json:"uri"`
|
||||
|
||||
// Used by simpleHttp, dvsni, and dns challenges
|
||||
Token string `json:"token,omitempty"`
|
||||
|
|
@ -431,6 +433,18 @@ type Authorization struct {
|
|||
Combinations [][]int `json:"combinations,omitempty" db:"combinations"`
|
||||
}
|
||||
|
||||
// FindChallenge will look for the given challenge inside this authorization. If
|
||||
// found, it will return the index of that challenge within the Authorization's
|
||||
// Challenges array. Otherwise it will return -1.
|
||||
func (authz *Authorization) FindChallenge(challengeID int64) int {
|
||||
for i, c := range authz.Challenges {
|
||||
if c.ID == challengeID {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// JSONBuffer fields get encoded and decoded JOSE-style, in base64url encoding
|
||||
// with stripped padding.
|
||||
type JSONBuffer []byte
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -31,8 +30,6 @@ type RegistrationAuthorityImpl struct {
|
|||
DNSResolver core.DNSResolver
|
||||
clk clock.Clock
|
||||
log *blog.AuditLogger
|
||||
|
||||
AuthzBase string
|
||||
}
|
||||
|
||||
// NewRegistrationAuthorityImpl constructs a new RA object.
|
||||
|
|
@ -150,6 +147,11 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization
|
|||
// Create validations, but we have to update them with URIs later
|
||||
challenges, combinations := ra.PA.ChallengesFor(identifier)
|
||||
|
||||
for i, _ := range challenges {
|
||||
// Add the account key used to generate the challenge
|
||||
challenges[i].AccountKey = ®.Key
|
||||
}
|
||||
|
||||
// Partially-filled object
|
||||
authz = core.Authorization{
|
||||
Identifier: identifier,
|
||||
|
|
@ -165,33 +167,19 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization
|
|||
// InternalServerError since the user-data was validated before being
|
||||
// passed to the SA.
|
||||
err = core.InternalServerError(fmt.Sprintf("Invalid authorization request: %s", err))
|
||||
return authz, err
|
||||
return core.Authorization{}, err
|
||||
}
|
||||
|
||||
// Construct all the challenge URIs
|
||||
for i := range authz.Challenges {
|
||||
// Ignoring these errors because we construct the URLs to be correct
|
||||
challengeURI, _ := core.ParseAcmeURL(ra.AuthzBase + authz.ID + "?challenge=" + strconv.Itoa(i))
|
||||
authz.Challenges[i].URI = challengeURI
|
||||
|
||||
// Add the account key used to generate the challenge
|
||||
authz.Challenges[i].AccountKey = ®.Key
|
||||
|
||||
if !authz.Challenges[i].IsSane(false) {
|
||||
// Check each challenge for sanity.
|
||||
for _, challenge := range authz.Challenges {
|
||||
if !challenge.IsSane(false) {
|
||||
// InternalServerError because we generated these challenges, they should
|
||||
// be OK.
|
||||
err = core.InternalServerError(fmt.Sprintf("Challenge didn't pass sanity check: %+v", authz.Challenges[i]))
|
||||
return authz, err
|
||||
err = core.InternalServerError(fmt.Sprintf("Challenge didn't pass sanity check: %+v", challenge))
|
||||
return core.Authorization{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Store the authorization object, then return it
|
||||
err = ra.SA.UpdatePendingAuthorization(authz)
|
||||
if err != nil {
|
||||
// InternalServerError because we created the authorization just above,
|
||||
// and adding Sane challenges should not break it.
|
||||
err = core.InternalServerError(err.Error())
|
||||
}
|
||||
return authz, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -214,7 +214,6 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut
|
|||
ra.VA = va
|
||||
ra.CA = &ca
|
||||
ra.PA = pa
|
||||
ra.AuthzBase = "http://acme.invalid/authz/"
|
||||
ra.DNSResolver = &mocks.MockDNS{}
|
||||
|
||||
AuthzInitial.RegistrationID = Registration.ID
|
||||
|
|
|
|||
|
|
@ -366,6 +366,7 @@ func (rpc *AmqpRPCServer) processMessage(msg amqp.Delivery) {
|
|||
CorrelationId: msg.CorrelationId,
|
||||
Type: msg.Type,
|
||||
Body: jsonResponse, // XXX-JWS: jws.Sign(privKey, body)
|
||||
Expiration: "30000",
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -486,7 +487,12 @@ func NewAmqpRPCClient(clientQueuePrefix, serverQueue string, channel *amqp.Chann
|
|||
return nil, err
|
||||
}
|
||||
|
||||
clientQueue := fmt.Sprintf("%s.%s", clientQueuePrefix, hostname)
|
||||
randID := make([]byte, 3)
|
||||
_, err = rand.Read(randID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientQueue := fmt.Sprintf("%s.%s.%x", clientQueuePrefix, hostname, randID)
|
||||
|
||||
rpc = &AmqpRPCCLient{
|
||||
serverQueue: serverQueue,
|
||||
|
|
@ -558,6 +564,7 @@ func (rpc *AmqpRPCCLient) Dispatch(method string, body []byte) chan []byte {
|
|||
ReplyTo: rpc.clientQueue,
|
||||
Type: method,
|
||||
Body: body, // XXX-JWS: jws.Sign(privKey, body)
|
||||
Expiration: "30000",
|
||||
})
|
||||
|
||||
return responseChan
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
-- +goose Up
|
||||
-- SQL in section 'Up' is executed when this migration is applied
|
||||
|
||||
ALTER TABLE `challenges` DROP COLUMN `uri`;
|
||||
|
||||
|
||||
-- +goose Down
|
||||
-- SQL section 'Down' is executed when this migration is rolled back
|
||||
|
||||
ALTER TABLE `challenges` ADD COLUMN (
|
||||
`uri` varchar(255)
|
||||
);
|
||||
16
sa/model.go
16
sa/model.go
|
|
@ -36,7 +36,6 @@ type challModel struct {
|
|||
Status core.AcmeStatus `db:"status"`
|
||||
Error []byte `db:"error"`
|
||||
Validated *time.Time `db:"validated"`
|
||||
URI string `db:"uri"`
|
||||
Token string `db:"token"`
|
||||
TLS *bool `db:"tls"`
|
||||
Validation []byte `db:"validation"`
|
||||
|
|
@ -85,6 +84,7 @@ func modelToRegistration(rm *regModel) (core.Registration, error) {
|
|||
|
||||
func challengeToModel(c *core.Challenge, authID string) (*challModel, error) {
|
||||
cm := challModel{
|
||||
ID: c.ID,
|
||||
AuthorizationID: authID,
|
||||
Type: c.Type,
|
||||
Status: c.Status,
|
||||
|
|
@ -108,12 +108,6 @@ func challengeToModel(c *core.Challenge, authID string) (*challModel, error) {
|
|||
}
|
||||
cm.Error = errJSON
|
||||
}
|
||||
if c.URI != nil {
|
||||
if len(c.URI.String()) > 255 {
|
||||
return nil, fmt.Errorf("URI is too long to store in the database")
|
||||
}
|
||||
cm.URI = c.URI.String()
|
||||
}
|
||||
if len(c.ValidationRecord) > 0 {
|
||||
vrJSON, err := json.Marshal(c.ValidationRecord)
|
||||
if err != nil {
|
||||
|
|
@ -139,19 +133,13 @@ func challengeToModel(c *core.Challenge, authID string) (*challModel, error) {
|
|||
|
||||
func modelToChallenge(cm *challModel) (core.Challenge, error) {
|
||||
c := core.Challenge{
|
||||
ID: cm.ID,
|
||||
Type: cm.Type,
|
||||
Status: cm.Status,
|
||||
Validated: cm.Validated,
|
||||
Token: cm.Token,
|
||||
TLS: cm.TLS,
|
||||
}
|
||||
if len(cm.URI) > 0 {
|
||||
uri, err := core.ParseAcmeURL(cm.URI)
|
||||
if err != nil {
|
||||
return core.Challenge{}, err
|
||||
}
|
||||
c.URI = uri
|
||||
}
|
||||
if len(cm.Validation) > 0 {
|
||||
val, err := jose.ParseSigned(string(cm.Validation))
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -440,17 +440,28 @@ func (ssa *SQLStorageAuthority) NewPendingAuthorization(authz core.Authorization
|
|||
return
|
||||
}
|
||||
|
||||
for _, c := range authz.Challenges {
|
||||
chall, err := challengeToModel(&c, pendingAuthz.ID)
|
||||
for i, c := range authz.Challenges {
|
||||
challModel, err := challengeToModel(&c, pendingAuthz.ID)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return core.Authorization{}, err
|
||||
}
|
||||
err = tx.Insert(chall)
|
||||
// Magic happens here: Gorp will modify challModel, setting challModel.ID
|
||||
// to the auto-increment primary key. This is important because we want
|
||||
// the challenge objects inside the Authorization we return to know their
|
||||
// IDs, so they can have proper URLs.
|
||||
// See https://godoc.org/github.com/coopernurse/gorp#DbMap.Insert
|
||||
err = tx.Insert(challModel)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return core.Authorization{}, err
|
||||
}
|
||||
challenge, err := modelToChallenge(challModel)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return core.Authorization{}, err
|
||||
}
|
||||
authz.Challenges[i] = challenge
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
|
|
|
|||
|
|
@ -177,9 +177,7 @@ func CreateDomainAuthWithRegId(t *testing.T, domainName string, sa *SQLStorageAu
|
|||
test.Assert(t, authz.ID != "", "ID shouldn't be blank")
|
||||
|
||||
// prepare challenge for auth
|
||||
u, err := core.ParseAcmeURL(domainName)
|
||||
test.AssertNotError(t, err, "Couldn't parse domainName "+domainName)
|
||||
chall := core.Challenge{Type: "simpleHttp", Status: core.StatusValid, URI: u, Token: "THISWOULDNTBEAGOODTOKEN"}
|
||||
chall := core.Challenge{Type: "simpleHttp", Status: core.StatusValid, URI: domainName, Token: "THISWOULDNTBEAGOODTOKEN"}
|
||||
combos := make([][]int, 1)
|
||||
combos[0] = []int{0, 1}
|
||||
exp := time.Now().AddDate(0, 0, 1) // expire in 1 day
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import (
|
|||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"regexp"
|
||||
|
|
@ -32,6 +31,7 @@ const (
|
|||
RegPath = "/acme/reg/"
|
||||
NewAuthzPath = "/acme/new-authz"
|
||||
AuthzPath = "/acme/authz/"
|
||||
ChallengePath = "/acme/challenge/"
|
||||
NewCertPath = "/acme/new-cert"
|
||||
CertPath = "/acme/cert/"
|
||||
RevokeCertPath = "/acme/revoke-cert"
|
||||
|
|
@ -47,13 +47,14 @@ type WebFrontEndImpl struct {
|
|||
log *blog.AuditLogger
|
||||
|
||||
// URL configuration parameters
|
||||
BaseURL string
|
||||
NewReg string
|
||||
RegBase string
|
||||
NewAuthz string
|
||||
AuthzBase string
|
||||
NewCert string
|
||||
CertBase string
|
||||
BaseURL string
|
||||
NewReg string
|
||||
RegBase string
|
||||
NewAuthz string
|
||||
AuthzBase string
|
||||
ChallengeBase string
|
||||
NewCert string
|
||||
CertBase string
|
||||
|
||||
// JSON encoded endpoint directory
|
||||
DirectoryJSON []byte
|
||||
|
|
@ -195,6 +196,7 @@ func (wfe *WebFrontEndImpl) Handler() (http.Handler, error) {
|
|||
wfe.RegBase = wfe.BaseURL + RegPath
|
||||
wfe.NewAuthz = wfe.BaseURL + NewAuthzPath
|
||||
wfe.AuthzBase = wfe.BaseURL + AuthzPath
|
||||
wfe.ChallengeBase = wfe.BaseURL + ChallengePath
|
||||
wfe.NewCert = wfe.BaseURL + NewCertPath
|
||||
wfe.CertBase = wfe.BaseURL + CertPath
|
||||
|
||||
|
|
@ -212,18 +214,22 @@ func (wfe *WebFrontEndImpl) Handler() (http.Handler, error) {
|
|||
wfe.DirectoryJSON = directoryJSON
|
||||
|
||||
m := http.NewServeMux()
|
||||
wfe.HandleFunc(m, "/", wfe.Index, "GET")
|
||||
wfe.HandleFunc(m, DirectoryPath, wfe.Directory, "GET")
|
||||
wfe.HandleFunc(m, NewRegPath, wfe.NewRegistration, "POST")
|
||||
wfe.HandleFunc(m, NewAuthzPath, wfe.NewAuthorization, "POST")
|
||||
wfe.HandleFunc(m, NewCertPath, wfe.NewCertificate, "POST")
|
||||
wfe.HandleFunc(m, RegPath, wfe.Registration, "POST")
|
||||
wfe.HandleFunc(m, AuthzPath, wfe.Authorization, "GET", "POST")
|
||||
wfe.HandleFunc(m, AuthzPath, wfe.Authorization, "GET")
|
||||
wfe.HandleFunc(m, ChallengePath, wfe.Challenge, "GET", "POST")
|
||||
wfe.HandleFunc(m, CertPath, wfe.Certificate, "GET")
|
||||
wfe.HandleFunc(m, RevokeCertPath, wfe.RevokeCertificate, "POST")
|
||||
wfe.HandleFunc(m, TermsPath, wfe.Terms, "GET")
|
||||
wfe.HandleFunc(m, IssuerPath, wfe.Issuer, "GET")
|
||||
wfe.HandleFunc(m, BuildIDPath, wfe.BuildID, "GET")
|
||||
// We don't use our special HandleFunc for "/" because it matches everything,
|
||||
// meaning we can wind up returning 405 when we mean to return 404. See
|
||||
// https://github.com/letsencrypt/boulder/issues/717
|
||||
m.HandleFunc("/", wfe.Index)
|
||||
return m, nil
|
||||
}
|
||||
|
||||
|
|
@ -243,16 +249,22 @@ func (wfe *WebFrontEndImpl) Index(response http.ResponseWriter, request *http.Re
|
|||
return
|
||||
}
|
||||
|
||||
tmpl := template.Must(template.New("body").Parse(`<html>
|
||||
<body>
|
||||
This is an <a href="https://github.com/letsencrypt/acme-spec/">ACME</a>
|
||||
Certificate Authority running <a href="https://github.com/letsencrypt/boulder">Boulder</a>,
|
||||
New registration is available at <a href="{{.NewReg}}">{{.NewReg}}</a>.
|
||||
</body>
|
||||
</html>
|
||||
`))
|
||||
tmpl.Execute(response, wfe)
|
||||
if request.Method != "GET" {
|
||||
logEvent.Error = "Bad method"
|
||||
response.Header().Set("Allow", "GET")
|
||||
response.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
response.Header().Set("Content-Type", "text/html")
|
||||
response.Write([]byte(fmt.Sprintf(`<html>
|
||||
<body>
|
||||
This is an <a href="https://github.com/letsencrypt/acme-spec/">ACME</a>
|
||||
Certificate Authority running <a href="https://github.com/letsencrypt/boulder">Boulder</a>.
|
||||
JSON directory is available at <a href="%s">%s</a>.
|
||||
</body>
|
||||
</html>
|
||||
`, DirectoryPath, DirectoryPath)))
|
||||
addCacheHeader(response, wfe.IndexCacheDuration.Seconds())
|
||||
}
|
||||
|
||||
|
|
@ -265,6 +277,7 @@ func addCacheHeader(w http.ResponseWriter, age float64) {
|
|||
}
|
||||
|
||||
func (wfe *WebFrontEndImpl) Directory(response http.ResponseWriter, request *http.Request) {
|
||||
response.Header().Set("Content-Type", "application/json")
|
||||
response.Write(wfe.DirectoryJSON)
|
||||
}
|
||||
|
||||
|
|
@ -554,8 +567,7 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque
|
|||
|
||||
// Make a URL for this authz, then blow away the ID and RegID before serializing
|
||||
authzURL := wfe.AuthzBase + string(authz.ID)
|
||||
authz.ID = ""
|
||||
authz.RegistrationID = 0
|
||||
wfe.prepAuthorizationForDisplay(&authz)
|
||||
responseBody, err := json.Marshal(authz)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
|
|
@ -773,46 +785,93 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request
|
|||
wfe.Stats.Inc("Certificates", 1, 1.0)
|
||||
}
|
||||
|
||||
func (wfe *WebFrontEndImpl) challenge(
|
||||
func (wfe *WebFrontEndImpl) Challenge(
|
||||
response http.ResponseWriter,
|
||||
request *http.Request,
|
||||
authz core.Authorization,
|
||||
logEvent *requestEvent) {
|
||||
request *http.Request) {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
|
||||
// Check that the requested challenge exists within the authorization
|
||||
found := false
|
||||
var challengeIndex int
|
||||
var challenge core.Challenge
|
||||
for i, challenge := range authz.Challenges {
|
||||
tempURL := challenge.URI
|
||||
if tempURL.Path == request.URL.Path && tempURL.RawQuery == request.URL.RawQuery {
|
||||
found = true
|
||||
challengeIndex = i
|
||||
break
|
||||
}
|
||||
notFound := func() {
|
||||
wfe.sendError(response, "No such registration", request.URL.Path, http.StatusNotFound)
|
||||
}
|
||||
|
||||
if !found {
|
||||
logEvent.Error = "Unable to find challenge"
|
||||
wfe.sendError(response, logEvent.Error, request.URL.RawQuery, http.StatusNotFound)
|
||||
// Challenge URIs are of the form /acme/challenge/<auth id>/<challenge id>.
|
||||
// Here we parse out the id components. TODO: Use a better tool to parse out
|
||||
// URL structure: https://github.com/letsencrypt/boulder/issues/437
|
||||
slug := strings.Split(request.URL.Path[len(ChallengePath):], "/")
|
||||
if len(slug) != 2 {
|
||||
notFound()
|
||||
return
|
||||
}
|
||||
authorizationID := slug[0]
|
||||
challengeID, err := strconv.ParseInt(slug[1], 10, 64)
|
||||
if err != nil {
|
||||
notFound()
|
||||
return
|
||||
}
|
||||
logEvent.Extra["AuthorizationID"] = authorizationID
|
||||
logEvent.Extra["ChallengeID"] = challengeID
|
||||
|
||||
authz, err := wfe.SA.GetAuthorization(authorizationID)
|
||||
if err != nil {
|
||||
notFound()
|
||||
return
|
||||
}
|
||||
// Check that the requested challenge exists within the authorization
|
||||
challengeIndex := authz.FindChallenge(challengeID)
|
||||
if challengeIndex == -1 {
|
||||
notFound()
|
||||
return
|
||||
}
|
||||
challenge := authz.Challenges[challengeIndex]
|
||||
|
||||
logEvent.Extra["ChallengeType"] = challenge.Type
|
||||
logEvent.Extra["AuthorizationRegistrationID"] = authz.RegistrationID
|
||||
logEvent.Extra["AuthorizationIdentifier"] = authz.Identifier
|
||||
logEvent.Extra["AuthorizationStatus"] = authz.Status
|
||||
logEvent.Extra["AuthorizationExpires"] = authz.Expires
|
||||
|
||||
switch request.Method {
|
||||
case "GET":
|
||||
wfe.getChallenge(response, request, authz, challenge, logEvent)
|
||||
wfe.getChallenge(response, request, authz, &challenge, &logEvent)
|
||||
|
||||
case "POST":
|
||||
wfe.postChallenge(response, request, authz, challengeIndex, logEvent)
|
||||
wfe.postChallenge(response, request, authz, challengeIndex, &logEvent)
|
||||
}
|
||||
}
|
||||
|
||||
// prepChallengeForDisplay takes a core.Challenge and prepares it for display to
|
||||
// the client by filling in its URI field and clearing its AccountKey and ID
|
||||
// fields.
|
||||
// TODO: Come up with a cleaner way to do this.
|
||||
// https://github.com/letsencrypt/boulder/issues/761
|
||||
func (wfe *WebFrontEndImpl) prepChallengeForDisplay(authz core.Authorization, challenge *core.Challenge) {
|
||||
challenge.URI = fmt.Sprintf("%s%s/%d", wfe.ChallengeBase, authz.ID, challenge.ID)
|
||||
challenge.AccountKey = nil
|
||||
// 0 is considered "empty" for the purpose of the JSON omitempty tag.
|
||||
challenge.ID = 0
|
||||
}
|
||||
|
||||
// prepAuthorizationForDisplay takes a core.Authorization and prepares it for
|
||||
// display to the client by clearing its ID and RegistrationID fields, and
|
||||
// preparing all its challenges.
|
||||
func (wfe *WebFrontEndImpl) prepAuthorizationForDisplay(authz *core.Authorization) {
|
||||
for i, _ := range authz.Challenges {
|
||||
wfe.prepChallengeForDisplay(*authz, &authz.Challenges[i])
|
||||
}
|
||||
authz.ID = ""
|
||||
authz.RegistrationID = 0
|
||||
}
|
||||
|
||||
func (wfe *WebFrontEndImpl) getChallenge(
|
||||
response http.ResponseWriter,
|
||||
request *http.Request,
|
||||
authz core.Authorization,
|
||||
challenge core.Challenge,
|
||||
challenge *core.Challenge,
|
||||
logEvent *requestEvent) {
|
||||
|
||||
wfe.prepChallengeForDisplay(authz, challenge)
|
||||
|
||||
jsonReply, err := json.Marshal(challenge)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
|
|
@ -823,7 +882,7 @@ func (wfe *WebFrontEndImpl) getChallenge(
|
|||
}
|
||||
|
||||
authzURL := wfe.AuthzBase + string(authz.ID)
|
||||
response.Header().Add("Location", challenge.URI.String())
|
||||
response.Header().Add("Location", challenge.URI)
|
||||
response.Header().Set("Content-Type", "application/json")
|
||||
response.Header().Add("Link", link(authzURL, "up"))
|
||||
response.WriteHeader(http.StatusAccepted)
|
||||
|
|
@ -890,6 +949,7 @@ func (wfe *WebFrontEndImpl) postChallenge(
|
|||
|
||||
// assumption: UpdateAuthorization does not modify order of challenges
|
||||
challenge := updatedAuthorization.Challenges[challengeIndex]
|
||||
wfe.prepChallengeForDisplay(authz, &challenge)
|
||||
jsonReply, err := json.Marshal(challenge)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
|
|
@ -899,7 +959,7 @@ func (wfe *WebFrontEndImpl) postChallenge(
|
|||
}
|
||||
|
||||
authzURL := wfe.AuthzBase + string(authz.ID)
|
||||
response.Header().Add("Location", challenge.URI.String())
|
||||
response.Header().Add("Location", challenge.URI)
|
||||
response.Header().Set("Content-Type", "application/json")
|
||||
response.Header().Add("Link", link(authzURL, "up"))
|
||||
response.WriteHeader(http.StatusAccepted)
|
||||
|
|
@ -1013,31 +1073,8 @@ func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request
|
|||
logEvent.Extra["AuthorizationStatus"] = authz.Status
|
||||
logEvent.Extra["AuthorizationExpires"] = authz.Expires
|
||||
|
||||
// If there is a fragment, then this is actually a request to a challenge URI
|
||||
if len(request.URL.RawQuery) != 0 {
|
||||
wfe.challenge(response, request, authz, &logEvent)
|
||||
} else if request.Method == "GET" {
|
||||
wfe.GetAuthorization(response, request, authz, &logEvent)
|
||||
} else {
|
||||
// For challenges, POST and GET are allowed. For authorizations only GET is
|
||||
// allowed.
|
||||
// TODO(jsha): Split challenge updates into a different path so we can use
|
||||
// the HandleFunc functionality for declaring allowed methods.
|
||||
// https://github.com/letsencrypt/boulder/issues/638
|
||||
logEvent.Error = "Method not allowed"
|
||||
response.Header().Set("Allow", "GET")
|
||||
wfe.sendError(response, logEvent.Error, request.Method, http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
wfe.prepAuthorizationForDisplay(&authz)
|
||||
|
||||
func (wfe *WebFrontEndImpl) GetAuthorization(
|
||||
response http.ResponseWriter,
|
||||
request *http.Request,
|
||||
authz core.Authorization,
|
||||
logEvent *requestEvent) {
|
||||
// Blank out ID and regID
|
||||
authz.ID = ""
|
||||
authz.RegistrationID = 0
|
||||
jsonReply, err := json.Marshal(authz)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
|
|
|
|||
|
|
@ -156,7 +156,20 @@ func (sa *MockSA) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Registration,
|
|||
func (sa *MockSA) GetAuthorization(id string) (core.Authorization, error) {
|
||||
if id == "valid" {
|
||||
exp := time.Now().AddDate(100, 0, 0)
|
||||
return core.Authorization{Status: core.StatusValid, RegistrationID: 1, Expires: &exp, Identifier: core.AcmeIdentifier{Type: "dns", Value: "not-an-example.com"}}, nil
|
||||
return core.Authorization{
|
||||
ID: "valid",
|
||||
Status: core.StatusValid,
|
||||
RegistrationID: 1,
|
||||
Expires: &exp,
|
||||
Identifier: core.AcmeIdentifier{Type: "dns", Value: "not-an-example.com"},
|
||||
Challenges: []core.Challenge{
|
||||
core.Challenge{
|
||||
ID: 23,
|
||||
Type: "dns",
|
||||
URI: "http://localhost:4300/acme/challenge/valid/23",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return core.Authorization{}, nil
|
||||
}
|
||||
|
|
@ -343,6 +356,7 @@ func setupWFE(t *testing.T) WebFrontEndImpl {
|
|||
wfe.RegBase = wfe.BaseURL + RegPath
|
||||
wfe.NewAuthz = wfe.BaseURL + NewAuthzPath
|
||||
wfe.AuthzBase = wfe.BaseURL + AuthzPath
|
||||
wfe.ChallengeBase = wfe.BaseURL + ChallengePath
|
||||
wfe.NewCert = wfe.BaseURL + NewCertPath
|
||||
wfe.CertBase = wfe.BaseURL + CertPath
|
||||
wfe.SubscriberAgreementURL = agreementURL
|
||||
|
|
@ -458,11 +472,11 @@ func TestStandardHeaders(t *testing.T) {
|
|||
path string
|
||||
allowed []string
|
||||
}{
|
||||
{"/", []string{"GET"}},
|
||||
{wfe.NewReg, []string{"POST"}},
|
||||
{wfe.RegBase, []string{"POST"}},
|
||||
{wfe.NewAuthz, []string{"POST"}},
|
||||
{wfe.AuthzBase, []string{"GET", "POST"}},
|
||||
{wfe.AuthzBase, []string{"GET"}},
|
||||
{wfe.ChallengeBase, []string{"GET", "POST"}},
|
||||
{wfe.NewCert, []string{"POST"}},
|
||||
{wfe.CertBase, []string{"GET"}},
|
||||
{wfe.SubscriberAgreementURL, []string{"GET"}},
|
||||
|
|
@ -484,6 +498,28 @@ func TestStandardHeaders(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestIndexPOST(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
responseWriter := httptest.NewRecorder()
|
||||
url, _ := url.Parse("/")
|
||||
wfe.Index(responseWriter, &http.Request{
|
||||
Method: "POST",
|
||||
URL: url,
|
||||
})
|
||||
test.AssertEquals(t, responseWriter.Code, http.StatusMethodNotAllowed)
|
||||
}
|
||||
|
||||
func TestPOST404(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
responseWriter := httptest.NewRecorder()
|
||||
url, _ := url.Parse("/foobar")
|
||||
wfe.Index(responseWriter, &http.Request{
|
||||
Method: "POST",
|
||||
URL: url,
|
||||
})
|
||||
test.AssertEquals(t, responseWriter.Code, http.StatusNotFound)
|
||||
}
|
||||
|
||||
func TestIndex(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe.IndexCacheDuration = time.Second * 10
|
||||
|
|
@ -497,8 +533,8 @@ func TestIndex(t *testing.T) {
|
|||
})
|
||||
test.AssertEquals(t, responseWriter.Code, http.StatusOK)
|
||||
test.AssertNotEquals(t, responseWriter.Body.String(), "404 page not found\n")
|
||||
test.Assert(t, strings.Contains(responseWriter.Body.String(), wfe.NewReg),
|
||||
"new-reg not found")
|
||||
test.Assert(t, strings.Contains(responseWriter.Body.String(), DirectoryPath),
|
||||
"directory path not found")
|
||||
test.AssertEquals(t, responseWriter.Header().Get("Cache-Control"), "public, max-age=10")
|
||||
|
||||
responseWriter.Body.Reset()
|
||||
|
|
@ -525,6 +561,7 @@ func TestDirectory(t *testing.T) {
|
|||
Method: "GET",
|
||||
URL: url,
|
||||
})
|
||||
test.AssertEquals(t, responseWriter.Header().Get("Content-Type"), "application/json")
|
||||
test.AssertEquals(t, responseWriter.Code, http.StatusOK)
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"new-authz":"http://localhost:4300/acme/new-authz","new-cert":"http://localhost:4300/acme/new-cert","new-reg":"http://localhost:4300/acme/new-reg","revoke-cert":"http://localhost:4300/acme/revoke-cert"}`)
|
||||
}
|
||||
|
|
@ -675,37 +712,21 @@ func TestChallenge(t *testing.T) {
|
|||
`), &key)
|
||||
test.AssertNotError(t, err, "Could not unmarshal testing key")
|
||||
|
||||
challengeURL := "/acme/authz/asdf?challenge=foo"
|
||||
challengeAcme := (*core.AcmeURL)(mustParseURL(challengeURL))
|
||||
authz := core.Authorization{
|
||||
ID: "asdf",
|
||||
Identifier: core.AcmeIdentifier{
|
||||
Type: "dns",
|
||||
Value: "letsencrypt.org",
|
||||
},
|
||||
Challenges: []core.Challenge{
|
||||
core.Challenge{
|
||||
Type: "dns",
|
||||
URI: challengeAcme,
|
||||
},
|
||||
},
|
||||
RegistrationID: 1,
|
||||
}
|
||||
|
||||
wfe.challenge(responseWriter,
|
||||
challengeURL := "/acme/challenge/valid/23"
|
||||
wfe.Challenge(responseWriter,
|
||||
makePostRequestWithPath(challengeURL,
|
||||
signRequest(t, `{"resource":"challenge"}`, &wfe.nonceService)),
|
||||
authz, &requestEvent{})
|
||||
signRequest(t, `{"resource":"challenge"}`, &wfe.nonceService)))
|
||||
|
||||
test.AssertEquals(t, responseWriter.Code, 202)
|
||||
test.AssertEquals(
|
||||
t, responseWriter.Header().Get("Location"),
|
||||
"/acme/authz/asdf?challenge=foo")
|
||||
challengeURL)
|
||||
test.AssertEquals(
|
||||
t, responseWriter.Header().Get("Link"),
|
||||
`</acme/authz/asdf>;rel="up"`)
|
||||
`</acme/authz/valid>;rel="up"`)
|
||||
test.AssertEquals(
|
||||
t, responseWriter.Body.String(),
|
||||
`{"type":"dns","uri":"/acme/authz/asdf?challenge=foo"}`)
|
||||
`{"type":"dns","uri":"/acme/challenge/valid/23"}`)
|
||||
}
|
||||
|
||||
func TestNewRegistration(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue