add context to WFE errors
This change makes the use of requestEvent more prominent and provides some nice handler types to make that better. In order to avoid stomping on Error, requestEvent gains an Errors string slice. Fixes #552
This commit is contained in:
parent
98fea98f4f
commit
7d04ea98cf
|
@ -0,0 +1,85 @@
|
|||
package wfe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
blog "github.com/letsencrypt/boulder/log"
|
||||
)
|
||||
|
||||
type requestEvent struct {
|
||||
ID string `json:",omitempty"`
|
||||
RealIP string `json:",omitempty"`
|
||||
ClientAddr string `json:",omitempty"`
|
||||
Endpoint string `json:",omitempty"`
|
||||
Method string `json:",omitempty"`
|
||||
RequestTime time.Time `json:",omitempty"`
|
||||
ResponseTime time.Time `json:",omitempty"`
|
||||
Errors []string
|
||||
Requester int64 `json:",omitempty"`
|
||||
Contacts []*core.AcmeURL `json:",omitempty"`
|
||||
|
||||
Extra map[string]interface{} `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (e *requestEvent) AddError(msg string, args ...interface{}) {
|
||||
e.Errors = append(e.Errors, fmt.Sprintf(msg, args...))
|
||||
}
|
||||
|
||||
type wfeHandlerFunc func(*requestEvent, http.ResponseWriter, *http.Request)
|
||||
|
||||
func (f wfeHandlerFunc) ServeHTTP(e *requestEvent, w http.ResponseWriter, r *http.Request) {
|
||||
f(e, w, r)
|
||||
}
|
||||
|
||||
type wfeHandler interface {
|
||||
ServeHTTP(e *requestEvent, w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
type wfeTopHandler struct {
|
||||
h wfeHandler
|
||||
log *blog.AuditLogger
|
||||
clk clock.Clock
|
||||
}
|
||||
|
||||
func (t *wfeTopHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
logEvent := &requestEvent{
|
||||
ID: core.NewToken(),
|
||||
RealIP: r.Header.Get("X-Real-IP"),
|
||||
ClientAddr: getClientAddr(r),
|
||||
Method: r.Method,
|
||||
RequestTime: time.Now(),
|
||||
Extra: make(map[string]interface{}, 0),
|
||||
}
|
||||
if r.URL != nil {
|
||||
logEvent.Endpoint = r.URL.String()
|
||||
}
|
||||
defer t.logEvent(logEvent)
|
||||
|
||||
t.h.ServeHTTP(logEvent, w, r)
|
||||
}
|
||||
|
||||
func (t *wfeTopHandler) logEvent(logEvent *requestEvent) {
|
||||
logEvent.ResponseTime = t.clk.Now()
|
||||
var msg string
|
||||
if len(logEvent.Errors) != 0 {
|
||||
msg = "Terminated request"
|
||||
} else {
|
||||
msg = "Successful request"
|
||||
}
|
||||
t.log.InfoObject(msg, logEvent)
|
||||
}
|
||||
|
||||
// Comma-separated list of HTTP clients involved in making this
|
||||
// request, starting with the original requestor and ending with the
|
||||
// remote end of our TCP connection (which is typically our own
|
||||
// proxy).
|
||||
func getClientAddr(r *http.Request) string {
|
||||
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
|
||||
return xff + "," + r.RemoteAddr
|
||||
}
|
||||
return r.RemoteAddr
|
||||
}
|
|
@ -19,6 +19,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
|
||||
jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/letsencrypt/go-jose"
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
blog "github.com/letsencrypt/boulder/log"
|
||||
|
@ -115,21 +116,6 @@ func statusCodeFromError(err interface{}) int {
|
|||
}
|
||||
}
|
||||
|
||||
type requestEvent struct {
|
||||
ID string `json:",omitempty"`
|
||||
RealIP string `json:",omitempty"`
|
||||
ClientAddr string `json:",omitempty"`
|
||||
Endpoint string `json:",omitempty"`
|
||||
Method string `json:",omitempty"`
|
||||
RequestTime time.Time `json:",omitempty"`
|
||||
ResponseTime time.Time `json:",omitempty"`
|
||||
Error string `json:",omitempty"`
|
||||
Requester int64 `json:",omitempty"`
|
||||
Contacts []*core.AcmeURL `json:",omitempty"`
|
||||
|
||||
Extra map[string]interface{} `json:",omitempty"`
|
||||
}
|
||||
|
||||
// NewWebFrontEndImpl constructs a web service for Boulder
|
||||
func NewWebFrontEndImpl(stats statsd.Statter) (WebFrontEndImpl, error) {
|
||||
logger := blog.GetAuditLogger()
|
||||
|
@ -173,7 +159,7 @@ func (mrw BodylessResponseWriter) Write(buf []byte) (int, error) {
|
|||
// * Never send a body in response to a HEAD request. Anything
|
||||
// written by the handler will be discarded if the method is HEAD.
|
||||
// Also, all handlers that accept GET automatically accept HEAD.
|
||||
func (wfe *WebFrontEndImpl) HandleFunc(mux *http.ServeMux, pattern string, h func(http.ResponseWriter, *http.Request), methods ...string) {
|
||||
func (wfe *WebFrontEndImpl) HandleFunc(mux *http.ServeMux, pattern string, h wfeHandlerFunc, methods ...string) {
|
||||
methodsMap := make(map[string]bool)
|
||||
for _, m := range methods {
|
||||
methodsMap[m] = true
|
||||
|
@ -184,7 +170,10 @@ func (wfe *WebFrontEndImpl) HandleFunc(mux *http.ServeMux, pattern string, h fun
|
|||
methodsMap["HEAD"] = true
|
||||
}
|
||||
methodsStr := strings.Join(methods, ", ")
|
||||
mux.HandleFunc(pattern, func(response http.ResponseWriter, request *http.Request) {
|
||||
mux.Handle(pattern, &wfeTopHandler{
|
||||
log: wfe.log,
|
||||
clk: clock.Default(),
|
||||
h: wfeHandlerFunc(func(e *requestEvent, response http.ResponseWriter, request *http.Request) {
|
||||
// We do not propagate errors here, because (1) they should be
|
||||
// transient, and (2) they fail closed.
|
||||
nonce, err := wfe.nonceService.Nonce()
|
||||
|
@ -204,18 +193,18 @@ func (wfe *WebFrontEndImpl) HandleFunc(mux *http.ServeMux, pattern string, h fun
|
|||
}
|
||||
|
||||
if !methodsMap[request.Method] {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
logEvent.Error = "Method not allowed"
|
||||
msg := "Method not allowed"
|
||||
e.AddError(msg)
|
||||
response.Header().Set("Allow", methodsStr)
|
||||
wfe.sendError(response, logEvent.Error, request.Method, http.StatusMethodNotAllowed)
|
||||
wfe.sendError(response, msg, request.Method, http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
wfe.setCORSHeaders(response, request, "")
|
||||
|
||||
// Call the wrapped handler.
|
||||
h(response, request)
|
||||
h(e, response, request)
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -259,29 +248,30 @@ func (wfe *WebFrontEndImpl) Handler() (http.Handler, error) {
|
|||
// 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)
|
||||
m.Handle("/", &wfeTopHandler{
|
||||
log: wfe.log,
|
||||
clk: clock.Default(),
|
||||
h: wfeHandlerFunc(wfe.Index),
|
||||
})
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Method implementations
|
||||
|
||||
// Index serves a simple identification page. It is not part of the ACME spec.
|
||||
func (wfe *WebFrontEndImpl) Index(response http.ResponseWriter, request *http.Request) {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
|
||||
func (wfe *WebFrontEndImpl) Index(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) {
|
||||
// http://golang.org/pkg/net/http/#example_ServeMux_Handle
|
||||
// The "/" pattern matches everything, so we need to check
|
||||
// that we're at the root here.
|
||||
if request.URL.Path != "/" {
|
||||
logEvent.Error = "Resource not found"
|
||||
logEvent.AddError("Resource not found")
|
||||
http.NotFound(response, request)
|
||||
response.Header().Set("Content-Type", "application/problem+json")
|
||||
return
|
||||
}
|
||||
|
||||
if request.Method != "GET" {
|
||||
logEvent.Error = "Bad method"
|
||||
logEvent.AddError("Bad method")
|
||||
response.Header().Set("Allow", "GET")
|
||||
response.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
|
@ -309,7 +299,7 @@ func addCacheHeader(w http.ResponseWriter, age float64) {
|
|||
|
||||
// Directory is an HTTP request handler that simply provides the directory
|
||||
// object stored in the WFE's DirectoryJSON member.
|
||||
func (wfe *WebFrontEndImpl) Directory(response http.ResponseWriter, request *http.Request) {
|
||||
func (wfe *WebFrontEndImpl) Directory(e *requestEvent, response http.ResponseWriter, request *http.Request) {
|
||||
response.Header().Set("Content-Type", "application/json")
|
||||
response.Write(wfe.DirectoryJSON)
|
||||
}
|
||||
|
@ -326,39 +316,44 @@ const (
|
|||
)
|
||||
|
||||
// verifyPOST reads and parses the request body, looks up the Registration
|
||||
// corresponding to its JWK, verifies the JWS signature,
|
||||
// checks that the resource field is present and correct in the JWS protected
|
||||
// header, and returns the JWS payload bytes, the key used to verify, and the
|
||||
// corresponding Registration (or error).
|
||||
// If regCheck is false, verifyPOST will still try to look up a registration
|
||||
// object, and will return it if found. However, if no registration object is
|
||||
// found, verifyPOST will attempt to verify the JWS using the key in the JWS
|
||||
// headers, and return the key plus a dummy registration if successful. If a
|
||||
// caller passes regCheck = false, it should plan on validating the key itself.
|
||||
func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool, resource core.AcmeResource) ([]byte, *jose.JsonWebKey, core.Registration, error) {
|
||||
// corresponding to its JWK, verifies the JWS signature, checks that the
|
||||
// resource field is present and correct in the JWS protected header, and
|
||||
// returns the JWS payload bytes, the key used to verify, and the corresponding
|
||||
// Registration (or error). If regCheck is false, verifyPOST will still try to
|
||||
// look up a registration object, and will return it if found. However, if no
|
||||
// registration object is found, verifyPOST will attempt to verify the JWS using
|
||||
// the key in the JWS headers, and return the key plus a dummy registration if
|
||||
// successful. If a caller passes regCheck = false, it should plan on validating
|
||||
// the key itself. verifyPOST also appends its errors to requestEvent.Errors so
|
||||
// code calling it does not need to if they imediately return a response to the
|
||||
// user.
|
||||
func (wfe *WebFrontEndImpl) verifyPOST(e *requestEvent, request *http.Request, regCheck bool, resource core.AcmeResource) ([]byte, *jose.JsonWebKey, core.Registration, error) {
|
||||
var err error
|
||||
// TODO: We should return a pointer to a registration, which can be nil,
|
||||
// rather the a registration value with a sentinel value.
|
||||
// https://github.com/letsencrypt/boulder/issues/877
|
||||
reg := core.Registration{ID: -1}
|
||||
reg := core.Registration{ID: 0}
|
||||
|
||||
if _, ok := request.Header["Content-Length"]; !ok {
|
||||
err = core.LengthRequiredError("Content-Length header is required for POST.")
|
||||
wfe.log.Debug(err.Error())
|
||||
wfe.stats.Inc("WFE.HTTP.ClientErrors.LengthRequiredError", 1, 1.0)
|
||||
e.AddError("missing Content-Length header on POST")
|
||||
return nil, nil, reg, err
|
||||
}
|
||||
|
||||
// Read body
|
||||
if request.Body == nil {
|
||||
err = core.MalformedRequestError("No body on POST")
|
||||
wfe.log.Debug(err.Error())
|
||||
wfe.stats.Inc("WFE.Errors.NoPOSTBody", 1, 1.0)
|
||||
e.AddError("no body on POST")
|
||||
return nil, nil, reg, err
|
||||
}
|
||||
|
||||
bodyBytes, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
err = core.InternalServerError(err.Error())
|
||||
wfe.log.Debug(err.Error())
|
||||
err = core.InternalServerError("unable to read request body")
|
||||
wfe.stats.Inc("WFE.Errors.UnableToReadRequestBody", 1, 1.0)
|
||||
e.AddError("unable to read request body")
|
||||
return nil, nil, reg, err
|
||||
}
|
||||
|
||||
|
@ -367,7 +362,8 @@ func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool, res
|
|||
parsedJws, err := jose.ParseSigned(body)
|
||||
if err != nil {
|
||||
puberr := core.SignatureValidationError("Parse error reading JWS")
|
||||
wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error()))
|
||||
wfe.stats.Inc("WFE.Errors.UnableToParseJWS", 1, 1.0)
|
||||
e.AddError("could not JSON parse body into JWS: %s", err)
|
||||
return nil, nil, reg, puberr
|
||||
}
|
||||
|
||||
|
@ -378,19 +374,22 @@ func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool, res
|
|||
// *anyway*, so it could always lie about what key was used by faking
|
||||
// the signature itself.
|
||||
if len(parsedJws.Signatures) > 1 {
|
||||
err = core.SignatureValidationError("Too many signatures on POST")
|
||||
wfe.log.Debug(err.Error())
|
||||
err = core.SignatureValidationError("Too many signatures in POST body")
|
||||
wfe.stats.Inc("WFE.Errors.TooManyJWSSignaturesInPOST", 1, 1.0)
|
||||
e.AddError("too many signatures in POST body: %d", len(parsedJws.Signatures))
|
||||
return nil, nil, reg, err
|
||||
}
|
||||
if len(parsedJws.Signatures) == 0 {
|
||||
err = core.SignatureValidationError("POST JWS not signed")
|
||||
wfe.log.Debug(err.Error())
|
||||
wfe.stats.Inc("WFE.Errors.JWSNotSignedInPOST", 1, 1.0)
|
||||
e.AddError("no signatures in POST body")
|
||||
return nil, nil, reg, err
|
||||
}
|
||||
submittedKey := parsedJws.Signatures[0].Header.JsonWebKey
|
||||
if submittedKey == nil {
|
||||
err = core.SignatureValidationError("No JWK in JWS header")
|
||||
wfe.log.Debug(err.Error())
|
||||
wfe.stats.Inc("WFE.Errors.NoJWKInJWSSignatureHeader", 1, 1.0)
|
||||
e.AddError("no JWK in JWS signature header in POST body")
|
||||
return nil, nil, reg, err
|
||||
}
|
||||
|
||||
|
@ -404,34 +403,46 @@ func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool, res
|
|||
// are "good". But when we are verifying against any submitted key, we want
|
||||
// to check its quality before doing the verify.
|
||||
if err = core.GoodKey(submittedKey.Key); err != nil {
|
||||
wfe.stats.Inc("WFE.Errors.JWKRejectedByGoodKey", 1, 1.0)
|
||||
e.AddError("JWK in request was rejected by GoodKey: %s", err)
|
||||
return nil, nil, reg, err
|
||||
}
|
||||
key = submittedKey
|
||||
} else if err != nil {
|
||||
// For all other errors, or if regCheck is true, return error immediately.
|
||||
wfe.stats.Inc("WFE.Errors.UnableToGetRegistrationByKey", 1, 1.0)
|
||||
e.AddError("unable to fetch registration by the given JWK: %s", err)
|
||||
return nil, nil, reg, err
|
||||
} else {
|
||||
// If the lookup was successful, use that key.
|
||||
key = ®.Key
|
||||
e.Requester = reg.ID
|
||||
e.Contacts = reg.Contact
|
||||
}
|
||||
|
||||
payload, header, err := parsedJws.Verify(key)
|
||||
if err != nil {
|
||||
puberr := core.SignatureValidationError("JWS verification error")
|
||||
wfe.log.Debug(string(body))
|
||||
wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error()))
|
||||
wfe.stats.Inc("WFE.Errors.JWSVerificationFailed", 1, 1.0)
|
||||
n := len(body)
|
||||
if n > 100 {
|
||||
n = 100
|
||||
}
|
||||
e.AddError("verification of JWS with the JWK failed: %v; body: %s", err, body[:n])
|
||||
return nil, nil, reg, puberr
|
||||
}
|
||||
|
||||
// Check that the request has a known anti-replay nonce
|
||||
// i.e., Nonce is in protected header and
|
||||
if err != nil || len(header.Nonce) == 0 {
|
||||
wfe.stats.Inc("WFE.Errors.JWSMissingNonce", 1, 1.0)
|
||||
e.AddError("JWS is missing an anti-replay nonce")
|
||||
err = core.SignatureValidationError("JWS has no anti-replay nonce")
|
||||
wfe.log.Debug(err.Error())
|
||||
return nil, nil, reg, err
|
||||
} else if !wfe.nonceService.Valid(header.Nonce) {
|
||||
wfe.stats.Inc("WFE.Errors.JWSInvalidNonce", 1, 1.0)
|
||||
e.AddError("JWS has an invalid anti-replay nonce")
|
||||
err = core.SignatureValidationError(fmt.Sprintf("JWS has invalid anti-replay nonce"))
|
||||
wfe.log.Debug(err.Error())
|
||||
return nil, nil, reg, err
|
||||
}
|
||||
|
||||
|
@ -441,17 +452,20 @@ func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool, res
|
|||
}
|
||||
err = json.Unmarshal([]byte(payload), &parsedRequest)
|
||||
if err != nil {
|
||||
wfe.stats.Inc("WFE.Errors.UnparsableJWSPayload", 1, 1.0)
|
||||
e.AddError("unable to JSON parse resource from JWS payload: %s", err)
|
||||
puberr := core.SignatureValidationError("Request payload did not parse as JSON")
|
||||
wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error()))
|
||||
return nil, nil, reg, puberr
|
||||
}
|
||||
if parsedRequest.Resource == "" {
|
||||
wfe.stats.Inc("WFE.Errors.NoResourceInJWSPayload", 1, 1.0)
|
||||
e.AddError("JWS request payload does not specifiy a resource")
|
||||
err = core.MalformedRequestError("Request payload does not specify a resource")
|
||||
wfe.log.Debug(err.Error())
|
||||
return nil, nil, reg, err
|
||||
} else if resource != core.AcmeResource(parsedRequest.Resource) {
|
||||
err = core.MalformedRequestError(fmt.Sprintf("Request payload has invalid resource: %s != %s", parsedRequest.Resource, resource))
|
||||
wfe.log.Debug(err.Error())
|
||||
wfe.stats.Inc("WFE.Errors.MismatchedResourceInJWSPayload", 1, 1.0)
|
||||
e.AddError("JWS request payload does not match resource")
|
||||
err = core.MalformedRequestError(fmt.Sprintf("JWS resource payload does not match the HTTP resource: %s != %s", parsedRequest.Resource, resource))
|
||||
return nil, nil, reg, err
|
||||
}
|
||||
|
||||
|
@ -517,34 +531,33 @@ func link(url, relation string) string {
|
|||
}
|
||||
|
||||
// NewRegistration is used by clients to submit a new registration/account
|
||||
func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, request *http.Request) {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
func (wfe *WebFrontEndImpl) NewRegistration(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) {
|
||||
|
||||
body, key, _, err := wfe.verifyPOST(request, false, core.ResourceNewReg)
|
||||
body, key, _, err := wfe.verifyPOST(logEvent, request, false, core.ResourceNewReg)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
// verifyPOST handles its own setting of logEvent.Errors
|
||||
wfe.sendError(response, malformedJWS, err, statusCodeFromError(err))
|
||||
return
|
||||
}
|
||||
|
||||
if existingReg, err := wfe.SA.GetRegistrationByKey(*key); err == nil {
|
||||
logEvent.Error = "Registration key is already in use"
|
||||
logEvent.AddError("Registration key is already in use")
|
||||
response.Header().Set("Location", fmt.Sprintf("%s%d", wfe.RegBase, existingReg.ID))
|
||||
wfe.sendError(response, logEvent.Error, nil, http.StatusConflict)
|
||||
wfe.sendError(response, "Registration key is already in use", nil, http.StatusConflict)
|
||||
return
|
||||
}
|
||||
|
||||
var init core.Registration
|
||||
err = json.Unmarshal(body, &init)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to unmarshal Registration: %s", err)
|
||||
wfe.sendError(response, "Error unmarshaling JSON", err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if len(init.Agreement) > 0 && init.Agreement != wfe.SubscriberAgreementURL {
|
||||
logEvent.Error = fmt.Sprintf("Provided agreement URL [%s] does not match current agreement URL [%s]", init.Agreement, wfe.SubscriberAgreementURL)
|
||||
wfe.sendError(response, logEvent.Error, nil, http.StatusBadRequest)
|
||||
msg := fmt.Sprintf("Provided agreement URL [%s] does not match current agreement URL [%s]", init.Agreement, wfe.SubscriberAgreementURL)
|
||||
logEvent.AddError(msg)
|
||||
wfe.sendError(response, msg, nil, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
init.Key = *key
|
||||
|
@ -554,15 +567,15 @@ func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, reques
|
|||
if err == nil {
|
||||
init.InitialIP = net.ParseIP(host)
|
||||
} else {
|
||||
logEvent.Error = "Couldn't parse RemoteAddr"
|
||||
wfe.sendError(response, logEvent.Error, nil, http.StatusInternalServerError)
|
||||
logEvent.AddError("Couldn't parse RemoteAddr: %s", request.RemoteAddr)
|
||||
wfe.sendError(response, "couldn't parse the remote (that is, the client's) address", nil, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
reg, err := wfe.RA.NewRegistration(init)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to create new registration: %s", err)
|
||||
wfe.sendError(response, "Error creating new registration", err, statusCodeFromError(err))
|
||||
return
|
||||
}
|
||||
|
@ -574,7 +587,7 @@ func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, reques
|
|||
regURL := fmt.Sprintf("%s%d", wfe.RegBase, reg.ID)
|
||||
responseBody, err := json.Marshal(reg)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to marsh registration: %s", err)
|
||||
// StatusInternalServerError because we just created this registration, it should be OK.
|
||||
wfe.sendError(response, "Error marshaling registration", err, http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -592,13 +605,10 @@ func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, reques
|
|||
}
|
||||
|
||||
// NewAuthorization is used by clients to submit a new ID Authorization
|
||||
func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, request *http.Request) {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
|
||||
body, _, currReg, err := wfe.verifyPOST(request, true, core.ResourceNewAuthz)
|
||||
func (wfe *WebFrontEndImpl) NewAuthorization(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) {
|
||||
body, _, currReg, err := wfe.verifyPOST(logEvent, request, true, core.ResourceNewAuthz)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
// verifyPOST handles its own setting of logEvent.Errors
|
||||
respMsg := malformedJWS
|
||||
respCode := statusCodeFromError(err)
|
||||
if _, ok := err.(core.NoSuchRegistrationError); ok {
|
||||
|
@ -608,20 +618,18 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque
|
|||
wfe.sendError(response, respMsg, err, respCode)
|
||||
return
|
||||
}
|
||||
logEvent.Requester = currReg.ID
|
||||
logEvent.Contacts = currReg.Contact
|
||||
// Any version of the agreement is acceptable here. Version match is enforced in
|
||||
// wfe.Registration when agreeing the first time. Agreement updates happen
|
||||
// by mailing subscribers and don't require a registration update.
|
||||
if currReg.Agreement == "" {
|
||||
logEvent.Error = "Must agree to subscriber agreement before any further actions"
|
||||
wfe.sendError(response, logEvent.Error, nil, http.StatusForbidden)
|
||||
logEvent.AddError("Must agree to subscriber agreement before any further actions")
|
||||
wfe.sendError(response, "Must agree to subscriber agreement before any further actions", nil, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
var init core.Authorization
|
||||
if err = json.Unmarshal(body, &init); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to JSON unmarshal Authorization: %s", err)
|
||||
wfe.sendError(response, "Error unmarshaling JSON", err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
@ -630,7 +638,7 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque
|
|||
// Create new authz and return
|
||||
authz, err := wfe.RA.NewAuthorization(init, currReg.ID)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to create new authz: %s", err)
|
||||
wfe.sendError(response, "Error creating new authz", err, statusCodeFromError(err))
|
||||
return
|
||||
}
|
||||
|
@ -641,7 +649,7 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque
|
|||
wfe.prepAuthorizationForDisplay(&authz)
|
||||
responseBody, err := json.Marshal(authz)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to marshal authz: %s", err)
|
||||
// StatusInternalServerError because we generated the authz, it should be OK
|
||||
wfe.sendError(response, "Error marshaling authz", err, http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -652,40 +660,36 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque
|
|||
response.Header().Set("Content-Type", "application/json")
|
||||
response.WriteHeader(http.StatusCreated)
|
||||
if _, err = response.Write(responseBody); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError(err.Error())
|
||||
wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
// RevokeCertificate is used by clients to request the revocation of a cert.
|
||||
func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, request *http.Request) {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
func (wfe *WebFrontEndImpl) RevokeCertificate(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) {
|
||||
|
||||
// We don't ask verifyPOST to verify there is a correponding registration,
|
||||
// because anyone with the right private key can revoke a certificate.
|
||||
body, requestKey, registration, err := wfe.verifyPOST(request, false, core.ResourceRevokeCert)
|
||||
body, requestKey, registration, err := wfe.verifyPOST(logEvent, request, false, core.ResourceRevokeCert)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
// verifyPOST handles its own setting of logEvent.Errors
|
||||
wfe.sendError(response, malformedJWS, err, statusCodeFromError(err))
|
||||
return
|
||||
}
|
||||
logEvent.Requester = registration.ID
|
||||
logEvent.Contacts = registration.Contact
|
||||
|
||||
type RevokeRequest struct {
|
||||
CertificateDER core.JSONBuffer `json:"certificate"`
|
||||
}
|
||||
var revokeRequest RevokeRequest
|
||||
if err = json.Unmarshal(body, &revokeRequest); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to JSON unmarshal RevokeRequest: %s", err)
|
||||
wfe.log.Debug(fmt.Sprintf("Couldn't unmarshal in revoke request %s", string(body)))
|
||||
wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
providedCert, err := x509.ParseCertificate(revokeRequest.CertificateDER)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to parse revoke certificate DER: %s", err)
|
||||
wfe.log.Debug("Couldn't parse cert in revoke request.")
|
||||
wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
|
||||
return
|
||||
|
@ -700,7 +704,7 @@ func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, requ
|
|||
}
|
||||
parsedCertificate, err := x509.ParseCertificate(cert.DER)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to parse certificate DER: %s", err)
|
||||
// InternalServerError because this is a failure to decode from our DB.
|
||||
wfe.sendError(response, "Invalid certificate", err, http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -712,25 +716,24 @@ func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, requ
|
|||
|
||||
certStatus, err := wfe.SA.GetCertificateStatus(serial)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to get certificate status: %s", err)
|
||||
wfe.sendError(response, "Certificate status not yet available", err, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
logEvent.Extra["CertificateStatus"] = certStatus.Status
|
||||
|
||||
if certStatus.Status == core.OCSPStatusRevoked {
|
||||
logEvent.Error = "Certificate already revoked"
|
||||
wfe.sendError(response, logEvent.Error, "", http.StatusConflict)
|
||||
logEvent.AddError("Certificate already revoked: %#v", serial)
|
||||
wfe.sendError(response, "Certificate already revoked", "", http.StatusConflict)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement method of revocation by authorizations on account.
|
||||
if !(core.KeyDigestEquals(requestKey, parsedCertificate.PublicKey) ||
|
||||
registration.ID == cert.RegistrationID) {
|
||||
logEvent.Error = "Revocation request must be signed by private key of cert to be revoked, or by the account key of the account that issued it."
|
||||
wfe.log.Debug("Key mismatch for revoke")
|
||||
logEvent.AddError("Revocation request must be signed by private key of cert to be revoked, or by the account key of the account that issued it.")
|
||||
wfe.sendError(response,
|
||||
logEvent.Error,
|
||||
"Revocation request must be signed by private key of cert to be revoked, or by the account key of the account that issued it.",
|
||||
requestKey,
|
||||
http.StatusForbidden)
|
||||
return
|
||||
|
@ -739,7 +742,7 @@ func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, requ
|
|||
// Use revocation code 0, meaning "unspecified"
|
||||
err = wfe.RA.RevokeCertificateWithReg(*parsedCertificate, 0, registration.ID)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("failed to revoke certificate: %s", err)
|
||||
wfe.sendError(response, "Failed to revoke certificate", err, statusCodeFromError(err))
|
||||
} else {
|
||||
wfe.log.Debug(fmt.Sprintf("Revoked %v", serial))
|
||||
|
@ -762,13 +765,10 @@ func (wfe *WebFrontEndImpl) logCsr(request *http.Request, cr core.CertificateReq
|
|||
|
||||
// NewCertificate is used by clients to request the issuance of a cert for an
|
||||
// authorized identifier.
|
||||
func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request *http.Request) {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
|
||||
body, _, reg, err := wfe.verifyPOST(request, true, core.ResourceNewCert)
|
||||
func (wfe *WebFrontEndImpl) NewCertificate(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) {
|
||||
body, _, reg, err := wfe.verifyPOST(logEvent, request, true, core.ResourceNewCert)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
// verifyPOST handles its own setting of logEvent.Errors
|
||||
respMsg := malformedJWS
|
||||
respCode := statusCodeFromError(err)
|
||||
if _, ok := err.(core.NoSuchRegistrationError); ok {
|
||||
|
@ -778,20 +778,18 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request
|
|||
wfe.sendError(response, respMsg, err, respCode)
|
||||
return
|
||||
}
|
||||
logEvent.Requester = reg.ID
|
||||
logEvent.Contacts = reg.Contact
|
||||
// Any version of the agreement is acceptable here. Version match is enforced in
|
||||
// wfe.Registration when agreeing the first time. Agreement updates happen
|
||||
// by mailing subscribers and don't require a registration update.
|
||||
if reg.Agreement == "" {
|
||||
logEvent.Error = "Must agree to subscriber agreement before any further actions"
|
||||
wfe.sendError(response, logEvent.Error, nil, http.StatusForbidden)
|
||||
logEvent.AddError("Must agree to subscriber agreement before any further actions")
|
||||
wfe.sendError(response, "Must agree to subscriber agreement before any further actions", nil, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
var certificateRequest core.CertificateRequest
|
||||
if err = json.Unmarshal(body, &certificateRequest); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to JSON unmarshal CertificateRequest: %s", err)
|
||||
wfe.sendError(response, "Error unmarshaling certificate request", err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
@ -803,7 +801,7 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request
|
|||
// a bad key from the client is just a malformed request and doesn't need to
|
||||
// be audited.
|
||||
if err = core.GoodKey(certificateRequest.CSR.PublicKey); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("CSR public key failed GoodKey: %s", err)
|
||||
wfe.sendError(response, "Invalid key in certificate request", err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
@ -819,7 +817,7 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request
|
|||
// RA for secondary validation.
|
||||
cert, err := wfe.RA.NewCertificate(certificateRequest, reg.ID)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to create new cert: %s", err)
|
||||
wfe.sendError(response, "Error creating new cert", err, statusCodeFromError(err))
|
||||
return
|
||||
}
|
||||
|
@ -830,7 +828,7 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request
|
|||
// enumerate and mirror our certificates.
|
||||
parsedCertificate, err := x509.ParseCertificate([]byte(cert.DER))
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to parse certificate: %s", err)
|
||||
wfe.sendError(response,
|
||||
"Error creating new cert", err,
|
||||
http.StatusBadRequest)
|
||||
|
@ -845,7 +843,7 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request
|
|||
response.Header().Set("Content-Type", "application/pkix-cert")
|
||||
response.WriteHeader(http.StatusCreated)
|
||||
if _, err = response.Write(cert.DER); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError(err.Error())
|
||||
wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
|
||||
}
|
||||
}
|
||||
|
@ -853,10 +851,9 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request
|
|||
// Challenge handles POST requests to challenge URLs. Such requests are clients'
|
||||
// responses to the server's challenges.
|
||||
func (wfe *WebFrontEndImpl) Challenge(
|
||||
logEvent *requestEvent,
|
||||
response http.ResponseWriter,
|
||||
request *http.Request) {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
|
||||
notFound := func() {
|
||||
wfe.sendError(response, "No such registration", request.URL.Path, http.StatusNotFound)
|
||||
|
@ -900,10 +897,10 @@ func (wfe *WebFrontEndImpl) Challenge(
|
|||
|
||||
switch request.Method {
|
||||
case "GET", "HEAD":
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -941,7 +938,7 @@ func (wfe *WebFrontEndImpl) getChallenge(
|
|||
|
||||
jsonReply, err := json.Marshal(challenge)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to marshal challenge: %s", err)
|
||||
// InternalServerError because this is a failure to decode data passed in
|
||||
// by the caller, which got it from the DB.
|
||||
wfe.sendError(response, "Failed to marshal challenge", err, http.StatusInternalServerError)
|
||||
|
@ -955,7 +952,7 @@ func (wfe *WebFrontEndImpl) getChallenge(
|
|||
response.WriteHeader(http.StatusAccepted)
|
||||
if _, err := response.Write(jsonReply); err != nil {
|
||||
wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -966,9 +963,9 @@ func (wfe *WebFrontEndImpl) postChallenge(
|
|||
authz core.Authorization,
|
||||
challengeIndex int,
|
||||
logEvent *requestEvent) {
|
||||
body, _, currReg, err := wfe.verifyPOST(request, true, core.ResourceChallenge)
|
||||
body, _, currReg, err := wfe.verifyPOST(logEvent, request, true, core.ResourceChallenge)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
// verifyPOST handles its own setting of logEvent.Errors
|
||||
respMsg := malformedJWS
|
||||
respCode := http.StatusBadRequest
|
||||
if _, ok := err.(core.NoSuchRegistrationError); ok {
|
||||
|
@ -978,30 +975,28 @@ func (wfe *WebFrontEndImpl) postChallenge(
|
|||
wfe.sendError(response, respMsg, err, respCode)
|
||||
return
|
||||
}
|
||||
logEvent.Requester = currReg.ID
|
||||
logEvent.Contacts = currReg.Contact
|
||||
// Any version of the agreement is acceptable here. Version match is enforced in
|
||||
// wfe.Registration when agreeing the first time. Agreement updates happen
|
||||
// by mailing subscribers and don't require a registration update.
|
||||
if currReg.Agreement == "" {
|
||||
logEvent.Error = "Must agree to subscriber agreement before any further actions"
|
||||
wfe.sendError(response, logEvent.Error, nil, http.StatusForbidden)
|
||||
logEvent.AddError("Registration didn't agree to subscriber agreement before any further actions")
|
||||
wfe.sendError(response, "Registration didn't agree to subscriber agreement before any further actions", nil, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
// Check that the registration ID matching the key used matches
|
||||
// the registration ID on the authz object
|
||||
if currReg.ID != authz.RegistrationID {
|
||||
logEvent.Error = fmt.Sprintf("User: %v != Authorization: %v", currReg.ID, authz.RegistrationID)
|
||||
logEvent.AddError("User registration id: %d != Authorization registration id: %v", currReg.ID, authz.RegistrationID)
|
||||
wfe.sendError(response, "User registration ID doesn't match registration ID in authorization",
|
||||
logEvent.Error,
|
||||
"",
|
||||
http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
var challengeUpdate core.Challenge
|
||||
if err = json.Unmarshal(body, &challengeUpdate); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("error JSON unmarshalling challenge response: %s", err)
|
||||
wfe.sendError(response, "Error unmarshaling challenge response", err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
@ -1009,7 +1004,7 @@ func (wfe *WebFrontEndImpl) postChallenge(
|
|||
// Ask the RA to update this authorization
|
||||
updatedAuthorization, err := wfe.RA.UpdateAuthorization(authz, challengeIndex, challengeUpdate)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to update challenge: %s", err)
|
||||
wfe.sendError(response, "Unable to update challenge", err, statusCodeFromError(err))
|
||||
return
|
||||
}
|
||||
|
@ -1019,7 +1014,7 @@ func (wfe *WebFrontEndImpl) postChallenge(
|
|||
wfe.prepChallengeForDisplay(authz, &challenge)
|
||||
jsonReply, err := json.Marshal(challenge)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("failed to marshal challenge: %s", err)
|
||||
// StatusInternalServerError because we made the challenges, they should be OK
|
||||
wfe.sendError(response, "Failed to marshal challenge", err, http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -1031,20 +1026,18 @@ func (wfe *WebFrontEndImpl) postChallenge(
|
|||
response.Header().Add("Link", link(authzURL, "up"))
|
||||
response.WriteHeader(http.StatusAccepted)
|
||||
if _, err = response.Write(jsonReply); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError(err.Error())
|
||||
wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Registration is used by a client to submit an update to their registration.
|
||||
func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *http.Request) {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
func (wfe *WebFrontEndImpl) Registration(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) {
|
||||
|
||||
body, _, currReg, err := wfe.verifyPOST(request, true, core.ResourceRegistration)
|
||||
body, _, currReg, err := wfe.verifyPOST(logEvent, request, true, core.ResourceRegistration)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
// verifyPOST handles its own setting of logEvent.Errors
|
||||
respMsg := malformedJWS
|
||||
respCode := statusCodeFromError(err)
|
||||
if _, ok := err.(core.NoSuchRegistrationError); ok {
|
||||
|
@ -1054,38 +1047,37 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *
|
|||
wfe.sendError(response, respMsg, err, respCode)
|
||||
return
|
||||
}
|
||||
logEvent.Requester = currReg.ID
|
||||
logEvent.Contacts = currReg.Contact
|
||||
|
||||
// Requests to this handler should have a path that leads to a known
|
||||
// registration
|
||||
idStr := parseIDFromPath(request.URL.Path)
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("registration ID must be an integer, was %#v", idStr)
|
||||
wfe.sendError(response, "Registration ID must be an integer", err, http.StatusBadRequest)
|
||||
return
|
||||
} else if id <= 0 {
|
||||
logEvent.Error = "Registration ID must be a positive non-zero integer"
|
||||
wfe.sendError(response, logEvent.Error, id, http.StatusBadRequest)
|
||||
logEvent.AddError("Registration ID must be a positive non-zero integer, was %d", id)
|
||||
wfe.sendError(response, "Registration ID must be a positive non-zero integer", id, http.StatusBadRequest)
|
||||
return
|
||||
} else if id != currReg.ID {
|
||||
logEvent.Error = "Request signing key did not match registration key"
|
||||
wfe.sendError(response, logEvent.Error, "", http.StatusForbidden)
|
||||
logEvent.AddError("Request signing key did not match registration key: %d != %d", id, currReg.ID)
|
||||
wfe.sendError(response, "Request signing key did not match registration key", "", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
var update core.Registration
|
||||
err = json.Unmarshal(body, &update)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to JSON parse registration: %s", err)
|
||||
wfe.sendError(response, "Error unmarshaling registration", err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if len(update.Agreement) > 0 && update.Agreement != wfe.SubscriberAgreementURL {
|
||||
logEvent.Error = fmt.Sprintf("Provided agreement URL [%s] does not match current agreement URL [%s]", update.Agreement, wfe.SubscriberAgreementURL)
|
||||
wfe.sendError(response, logEvent.Error, nil, http.StatusBadRequest)
|
||||
msg := fmt.Sprintf("Provided agreement URL [%s] does not match current agreement URL [%s]", update.Agreement, wfe.SubscriberAgreementURL)
|
||||
logEvent.AddError(msg)
|
||||
wfe.sendError(response, msg, nil, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1098,14 +1090,14 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *
|
|||
// Ask the RA to update this authorization.
|
||||
updatedReg, err := wfe.RA.UpdateRegistration(currReg, update)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to update registration: %s", err)
|
||||
wfe.sendError(response, "Unable to update registration", err, statusCodeFromError(err))
|
||||
return
|
||||
}
|
||||
|
||||
jsonReply, err := json.Marshal(updatedReg)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to marshal updated registration: %s", err)
|
||||
// StatusInternalServerError because we just generated the reg, it should be OK
|
||||
wfe.sendError(response, "Failed to marshal registration", err, http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -1121,14 +1113,12 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *
|
|||
|
||||
// Authorization is used by clients to submit an update to one of their
|
||||
// authorizations.
|
||||
func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request *http.Request) {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
|
||||
func (wfe *WebFrontEndImpl) Authorization(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) {
|
||||
// Requests to this handler should have a path that leads to a known authz
|
||||
id := parseIDFromPath(request.URL.Path)
|
||||
authz, err := wfe.SA.GetAuthorization(id)
|
||||
if err != nil {
|
||||
logEvent.AddError("No such authorization at id %s", id)
|
||||
wfe.sendError(response,
|
||||
"Unable to find authorization", err,
|
||||
http.StatusNotFound)
|
||||
|
@ -1144,16 +1134,16 @@ func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request
|
|||
|
||||
jsonReply, err := json.Marshal(authz)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("Failed to JSON marshal authz: %s", err)
|
||||
// InternalServerError because this is a failure to decode from our DB.
|
||||
wfe.sendError(response, "Failed to marshal authz", err, http.StatusInternalServerError)
|
||||
wfe.sendError(response, "Failed to JSON marshal authz", err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
response.Header().Add("Link", link(wfe.NewCert, "next"))
|
||||
response.Header().Set("Content-Type", "application/json")
|
||||
response.WriteHeader(http.StatusOK)
|
||||
if _, err = response.Write(jsonReply); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to write authorization update response: %s", err)
|
||||
wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
|
||||
}
|
||||
}
|
||||
|
@ -1162,32 +1152,29 @@ var allHex = regexp.MustCompile("^[0-9a-f]+$")
|
|||
|
||||
// Certificate is used by clients to request a copy of their current certificate, or to
|
||||
// request a reissuance of the certificate.
|
||||
func (wfe *WebFrontEndImpl) Certificate(response http.ResponseWriter, request *http.Request) {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
func (wfe *WebFrontEndImpl) Certificate(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) {
|
||||
|
||||
path := request.URL.Path
|
||||
// Certificate paths consist of the CertBase path, plus exactly sixteen hex
|
||||
// digits.
|
||||
if !strings.HasPrefix(path, CertPath) {
|
||||
logEvent.Error = "Certificate not found"
|
||||
wfe.sendError(response, logEvent.Error, path, http.StatusNotFound)
|
||||
logEvent.AddError("this request path should not have gotten to Certificate: %#v is not a prefix of %#v", path, CertPath)
|
||||
wfe.sendError(response, "Certificate not found", path, http.StatusNotFound)
|
||||
addNoCacheHeader(response)
|
||||
return
|
||||
}
|
||||
serial := path[len(CertPath):]
|
||||
if !core.ValidSerial(serial) {
|
||||
logEvent.Error = "Certificate not found"
|
||||
wfe.sendError(response, logEvent.Error, serial, http.StatusNotFound)
|
||||
logEvent.AddError("certificate serial provided was not valid: %s", serial)
|
||||
wfe.sendError(response, "Certificate not found", serial, http.StatusNotFound)
|
||||
addNoCacheHeader(response)
|
||||
return
|
||||
}
|
||||
wfe.log.Debug(fmt.Sprintf("Requested certificate ID %s", serial))
|
||||
logEvent.Extra["RequestedSerial"] = serial
|
||||
|
||||
cert, err := wfe.SA.GetCertificate(serial)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to get certificate by serial id %#v: %s", serial, err)
|
||||
if strings.HasPrefix(err.Error(), "gorp: multiple rows returned") {
|
||||
wfe.sendError(response, "Multiple certificates with same short serial", err, http.StatusConflict)
|
||||
} else {
|
||||
|
@ -1204,7 +1191,7 @@ func (wfe *WebFrontEndImpl) Certificate(response http.ResponseWriter, request *h
|
|||
response.Header().Add("Link", link(IssuerPath, "up"))
|
||||
response.WriteHeader(http.StatusOK)
|
||||
if _, err = response.Write(cert.DER); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to write new certificate response: %s", err)
|
||||
wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
|
||||
}
|
||||
return
|
||||
|
@ -1212,39 +1199,30 @@ func (wfe *WebFrontEndImpl) Certificate(response http.ResponseWriter, request *h
|
|||
|
||||
// Terms is used by the client to obtain the current Terms of Service /
|
||||
// Subscriber Agreement to which the subscriber must agree.
|
||||
func (wfe *WebFrontEndImpl) Terms(response http.ResponseWriter, request *http.Request) {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
|
||||
func (wfe *WebFrontEndImpl) Terms(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) {
|
||||
http.Redirect(response, request, wfe.SubscriberAgreementURL, http.StatusFound)
|
||||
}
|
||||
|
||||
// Issuer obtains the issuer certificate used by this instance of Boulder.
|
||||
func (wfe *WebFrontEndImpl) Issuer(response http.ResponseWriter, request *http.Request) {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
|
||||
func (wfe *WebFrontEndImpl) Issuer(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) {
|
||||
addCacheHeader(response, wfe.IssuerCacheDuration.Seconds())
|
||||
|
||||
// TODO Content negotiation
|
||||
response.Header().Set("Content-Type", "application/pkix-cert")
|
||||
response.WriteHeader(http.StatusOK)
|
||||
if _, err := response.Write(wfe.IssuerCert); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to write issuer certificate response: %s", err)
|
||||
wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
// BuildID tells the requestor what build we're running.
|
||||
func (wfe *WebFrontEndImpl) BuildID(response http.ResponseWriter, request *http.Request) {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
|
||||
func (wfe *WebFrontEndImpl) BuildID(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) {
|
||||
response.Header().Set("Content-Type", "text/plain")
|
||||
response.WriteHeader(http.StatusOK)
|
||||
detailsString := fmt.Sprintf("Boulder=(%s %s)", core.GetBuildID(), core.GetBuildTime())
|
||||
if _, err := fmt.Fprintln(response, detailsString); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
logEvent.AddError("unable to print build information: %s", err)
|
||||
wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
|
||||
}
|
||||
}
|
||||
|
@ -1303,40 +1281,3 @@ func (wfe *WebFrontEndImpl) setCORSHeaders(response http.ResponseWriter, request
|
|||
response.Header().Set("Access-Control-Expose-Headers", "Link, Replay-Nonce")
|
||||
response.Header().Set("Access-Control-Max-Age", "86400")
|
||||
}
|
||||
|
||||
func (wfe *WebFrontEndImpl) logRequestDetails(logEvent *requestEvent) {
|
||||
logEvent.ResponseTime = time.Now()
|
||||
var msg string
|
||||
if logEvent.Error != "" {
|
||||
msg = "Terminated request"
|
||||
} else {
|
||||
msg = "Successful request"
|
||||
}
|
||||
wfe.log.InfoObject(msg, logEvent)
|
||||
}
|
||||
|
||||
func (wfe *WebFrontEndImpl) populateRequestEvent(request *http.Request) (logEvent requestEvent) {
|
||||
logEvent = requestEvent{
|
||||
ID: core.NewToken(),
|
||||
RealIP: request.Header.Get("X-Real-IP"),
|
||||
ClientAddr: getClientAddr(request),
|
||||
Method: request.Method,
|
||||
RequestTime: time.Now(),
|
||||
Extra: make(map[string]interface{}, 0),
|
||||
}
|
||||
if request.URL != nil {
|
||||
logEvent.Endpoint = request.URL.String()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Comma-separated list of HTTP clients involved in making this
|
||||
// request, starting with the original requestor and ending with the
|
||||
// remote end of our TCP connection (which is typically our own
|
||||
// proxy).
|
||||
func getClientAddr(r *http.Request) string {
|
||||
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
|
||||
return xff + "," + r.RemoteAddr
|
||||
}
|
||||
return r.RemoteAddr
|
||||
}
|
||||
|
|
|
@ -271,7 +271,7 @@ func TestHandleFunc(t *testing.T) {
|
|||
mux = http.NewServeMux()
|
||||
rw = httptest.NewRecorder()
|
||||
stubCalled = false
|
||||
wfe.HandleFunc(mux, "/test", func(http.ResponseWriter, *http.Request) {
|
||||
wfe.HandleFunc(mux, "/test", func(*requestEvent, http.ResponseWriter, *http.Request) {
|
||||
stubCalled = true
|
||||
}, allowed...)
|
||||
req.URL = mustParseURL("/test")
|
||||
|
@ -463,7 +463,7 @@ func TestIndexPOST(t *testing.T) {
|
|||
wfe := setupWFE(t)
|
||||
responseWriter := httptest.NewRecorder()
|
||||
url, _ := url.Parse("/")
|
||||
wfe.Index(responseWriter, &http.Request{
|
||||
wfe.Index(newRequestEvent(), responseWriter, &http.Request{
|
||||
Method: "POST",
|
||||
URL: url,
|
||||
})
|
||||
|
@ -474,7 +474,7 @@ func TestPOST404(t *testing.T) {
|
|||
wfe := setupWFE(t)
|
||||
responseWriter := httptest.NewRecorder()
|
||||
url, _ := url.Parse("/foobar")
|
||||
wfe.Index(responseWriter, &http.Request{
|
||||
wfe.Index(newRequestEvent(), responseWriter, &http.Request{
|
||||
Method: "POST",
|
||||
URL: url,
|
||||
})
|
||||
|
@ -488,7 +488,7 @@ func TestIndex(t *testing.T) {
|
|||
responseWriter := httptest.NewRecorder()
|
||||
|
||||
url, _ := url.Parse("/")
|
||||
wfe.Index(responseWriter, &http.Request{
|
||||
wfe.Index(newRequestEvent(), responseWriter, &http.Request{
|
||||
Method: "GET",
|
||||
URL: url,
|
||||
})
|
||||
|
@ -501,7 +501,7 @@ func TestIndex(t *testing.T) {
|
|||
responseWriter.Body.Reset()
|
||||
responseWriter.Header().Del("Cache-Control")
|
||||
url, _ = url.Parse("/foo")
|
||||
wfe.Index(responseWriter, &http.Request{
|
||||
wfe.Index(newRequestEvent(), responseWriter, &http.Request{
|
||||
URL: url,
|
||||
})
|
||||
//test.AssertEquals(t, responseWriter.Code, http.StatusNotFound)
|
||||
|
@ -566,7 +566,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
|
||||
// POST, but no body.
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewCertificate(responseWriter, &http.Request{
|
||||
wfe.NewCertificate(newRequestEvent(), responseWriter, &http.Request{
|
||||
Method: "POST",
|
||||
Header: map[string][]string{
|
||||
"Content-Length": []string{"0"},
|
||||
|
@ -578,14 +578,14 @@ func TestIssueCertificate(t *testing.T) {
|
|||
|
||||
// POST, but body that isn't valid JWS
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewCertificate(responseWriter, makePostRequest("hi"))
|
||||
wfe.NewCertificate(newRequestEvent(), responseWriter, makePostRequest("hi"))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Parse error reading JWS"}`)
|
||||
|
||||
// POST, Properly JWS-signed, but payload is "foo", not base64-encoded JSON.
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewCertificate(responseWriter,
|
||||
wfe.NewCertificate(newRequestEvent(), responseWriter,
|
||||
makePostRequest(signRequest(t, "foo", &wfe.nonceService)))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
|
@ -593,7 +593,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
|
||||
// Valid, signed JWS body, payload is '{}'
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewCertificate(responseWriter,
|
||||
wfe.NewCertificate(newRequestEvent(), responseWriter,
|
||||
makePostRequest(
|
||||
signRequest(t, "{}", &wfe.nonceService)))
|
||||
test.AssertEquals(t,
|
||||
|
@ -602,7 +602,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
|
||||
// Valid, signed JWS body, payload is '{"resource":"new-cert"}'
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewCertificate(responseWriter,
|
||||
wfe.NewCertificate(newRequestEvent(), responseWriter,
|
||||
makePostRequest(signRequest(t, `{"resource":"new-cert"}`, &wfe.nonceService)))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
|
@ -613,7 +613,8 @@ func TestIssueCertificate(t *testing.T) {
|
|||
// openssl req -outform der -new -nodes -key wfe/test/178.key -subj /CN=foo.com | \
|
||||
// sed 's/foo.com/fob.com/' | b64url
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewCertificate(responseWriter,
|
||||
wfe.NewCertificate(newRequestEvent(),
|
||||
responseWriter,
|
||||
makePostRequest(signRequest(t, `{
|
||||
"resource":"new-cert",
|
||||
"csr": "MIICVzCCAT8CAQAwEjEQMA4GA1UEAwwHZm9iLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKzHhqcMSTVjBu61vufGVmIYM4mMbWXgndHOUWnIqSKcNtFtPQ465tcZRT5ITIZWXGjsmgDrj31qvG3t5qLwyaF5hsTvFHK72nLMAQhdgM6481Qe9yaoaulWpkGr_9LVz4jQ9pGAaLVamXGpSxV-ipTOo79Sev4aZE8ksD9atEfWtcOD9w8_zj74vpWjTAHN49Q88chlChVqakn0zSfHPfS-jF8g0UTddBuF0Ti3sZChjxzbo6LwZ4182xX7XPnOLav3AGj0Su7j5XMl3OpenOrlWulWJeZIHq5itGW321j306XiGdbrdWH4K7JygICFds6oolwQRGBY6yinAtCgkTcCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQBxPiHOtKuBxtvecMNtLkTSuTyEkusQGnjoFDaKe5oqwGYQgy0YBii2-BbaPmqS4ZaDc-vDz_RLeKH5ZiH-NliYR1V_CRtpFLQi18g_2pLQnZLVO3ENs-SM37nU_nBGn9O93t2bkssoM3fZmtgp3R2W7I_wvx7Z8oWKa4boTeBAg_q9Gmi6QskZBddK7A4S_vOR0frU6QSPK_ksPhvovp9fwb6CVKrlJWf556UwRPWgbkW39hvTxK2KHhrUEg3oawNkWde2jZtnZ9e-9zpw8-_5O0X7-YN0ucbFTfQybce_ReuLlGepiHT5bvVavBZoIvqw1XOgSMvGgZFU8tAWMBlj"
|
||||
|
@ -626,7 +627,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
// openssl req -outform der -new -nodes -key wfe/test/178.key -subj /CN=meep.com | b64url
|
||||
mockLog.Clear()
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewCertificate(responseWriter,
|
||||
wfe.NewCertificate(newRequestEvent(), responseWriter,
|
||||
makePostRequest(signRequest(t, `{
|
||||
"resource":"new-cert",
|
||||
"csr": "MIICWDCCAUACAQAwEzERMA8GA1UEAwwIbWVlcC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaqzue57mgXEoGTZZoVkkCZraebWgXI8irX2BgQB1A3iZa9onxGPMcWQMxhSuUisbEJi4UkMcVST12HX01rUwhj41UuBxJvI1w4wvdstssTAaa9c9tsQ5-UED2bFRL1MsyBdbmCF_-pu3i-ZIYqWgiKbjVBe3nlAVbo77zizwp3Y4Tp1_TBOwTAuFkHePmkNT63uPm9My_hNzsSm1o-Q519Cf7ry-JQmOVgz_jIgFVGFYJ17EV3KUIpUuDShuyCFATBQspgJSN2DoXRUlQjXXkNTj23OxxdT_cVLcLJjytyG6e5izME2R2aCkDBWIc1a4_sRJ0R396auPXG6KhJ7o_AgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEALu046p76aKgvoAEHFINkMTgKokPXf9mZ4IZx_BKz-qs1MPMxVtPIrQDVweBH6tYT7Hfj2naLry6SpZ3vUNP_FYeTFWgW1V03LiqacX-QQgbEYtn99Dt3ScGyzb7EH833ztb3vDJ_-ha_CJplIrg-kHBBrlLFWXhh-I9K1qLRTNpbhZ18ooFde4Sbhkw9o9fKivGhx9aYr7ZbjRsNtKit_DsG1nwEXz53TMJ2vB9IQY29coJv_n5NFLkvBfzbG5faRNiFcimPYBO2jFdaA2mWzfxltLtwMF_dBwzTXDpMo3TVT9zEdV8YpsWqr63igqGDZVpKenlkqvRTeGJVayVuMA"
|
||||
|
@ -639,7 +640,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
mockLog.Clear()
|
||||
responseWriter.Body.Reset()
|
||||
// openssl req -outform der -new -nodes -key wfe/test/178.key -subj /CN=not-an-example.com | b64url
|
||||
wfe.NewCertificate(responseWriter,
|
||||
wfe.NewCertificate(newRequestEvent(), responseWriter,
|
||||
makePostRequest(signRequest(t, `{
|
||||
"resource":"new-cert",
|
||||
"csr": "MIICYjCCAUoCAQAwHTEbMBkGA1UEAwwSbm90LWFuLWV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmqs7nue5oFxKBk2WaFZJAma2nm1oFyPIq19gYEAdQN4mWvaJ8RjzHFkDMYUrlIrGxCYuFJDHFUk9dh19Na1MIY-NVLgcSbyNcOML3bLbLEwGmvXPbbEOflBA9mxUS9TLMgXW5ghf_qbt4vmSGKloIim41QXt55QFW6O-84s8Kd2OE6df0wTsEwLhZB3j5pDU-t7j5vTMv4Tc7EptaPkOdfQn-68viUJjlYM_4yIBVRhWCdexFdylCKVLg0obsghQEwULKYCUjdg6F0VJUI115DU49tzscXU_3FS3CyY8rchunuYszBNkdmgpAwViHNWuP7ESdEd_emrj1xuioSe6PwIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAE_T1nWU38XVYL28hNVSXU0rW5IBUKtbvr0qAkD4kda4HmQRTYkt-LNSuvxoZCC9lxijjgtJi-OJe_DCTdZZpYzewlVvcKToWSYHYQ6Wm1-fxxD_XzphvZOujpmBySchdiz7QSVWJmVZu34XD5RJbIcrmj_cjRt42J1hiTFjNMzQu9U6_HwIMmliDL-soFY2RTvvZf-dAFvOUQ-Wbxt97eM1PbbmxJNWRhbAmgEpe9PWDPTpqV5AK56VAa991cQ1P8ZVmPss5hvwGWhOtpnpTZVHN3toGNYFKqxWPboirqushQlfKiFqT9rpRgM3-mFjOHidGqsKEkTdmfSVlVEk3oo="
|
||||
|
@ -680,7 +681,7 @@ func TestGetChallenge(t *testing.T) {
|
|||
req, err := http.NewRequest(method, challengeURL, nil)
|
||||
test.AssertNotError(t, err, "Could not make NewRequest")
|
||||
|
||||
wfe.Challenge(resp, req)
|
||||
wfe.Challenge(newRequestEvent(), resp, req)
|
||||
test.AssertEquals(t,
|
||||
resp.Code,
|
||||
http.StatusAccepted)
|
||||
|
@ -722,7 +723,7 @@ func TestChallenge(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Could not unmarshal testing key")
|
||||
|
||||
challengeURL := "/acme/challenge/valid/23"
|
||||
wfe.Challenge(responseWriter,
|
||||
wfe.Challenge(newRequestEvent(), responseWriter,
|
||||
makePostRequestWithPath(challengeURL,
|
||||
signRequest(t, `{"resource":"challenge"}`, &wfe.nonceService)))
|
||||
|
||||
|
@ -842,7 +843,7 @@ func TestNewRegistration(t *testing.T) {
|
|||
nonce, err = wfe.nonceService.Nonce()
|
||||
test.AssertNotError(t, err, "Unable to create nonce")
|
||||
result, err = signer.Sign([]byte(`{"resource":"new-reg","contact":["tel:123456789"],"agreement":"`+agreementURL+`"}`), nonce)
|
||||
wfe.NewRegistration(responseWriter,
|
||||
wfe.NewRegistration(newRequestEvent(), responseWriter,
|
||||
makePostRequest(result.FullSerialize()))
|
||||
|
||||
var reg core.Registration
|
||||
|
@ -878,7 +879,7 @@ func TestNewRegistration(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Unable to create nonce")
|
||||
result, err = signer.Sign([]byte(`{"resource":"new-reg","contact":["tel:123456789"],"agreement":"`+agreementURL+`"}`), nonce)
|
||||
|
||||
wfe.NewRegistration(responseWriter,
|
||||
wfe.NewRegistration(newRequestEvent(), responseWriter,
|
||||
makePostRequest(result.FullSerialize()))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
|
@ -946,7 +947,7 @@ func TestRevokeCertificateCertKey(t *testing.T) {
|
|||
nonce, err := wfe.nonceService.Nonce()
|
||||
test.AssertNotError(t, err, "Unable to create nonce")
|
||||
result, _ := signer.Sign(revokeRequestJSON, nonce)
|
||||
wfe.RevokeCertificate(responseWriter,
|
||||
wfe.RevokeCertificate(newRequestEvent(), responseWriter,
|
||||
makePostRequest(result.FullSerialize()))
|
||||
test.AssertEquals(t, responseWriter.Code, 200)
|
||||
test.AssertEquals(t, responseWriter.Body.String(), "")
|
||||
|
@ -970,7 +971,7 @@ func TestRevokeCertificateAccountKey(t *testing.T) {
|
|||
nonce, err := wfe.nonceService.Nonce()
|
||||
test.AssertNotError(t, err, "Unable to create nonce")
|
||||
result, _ := accountKeySigner.Sign(revokeRequestJSON, nonce)
|
||||
wfe.RevokeCertificate(responseWriter,
|
||||
wfe.RevokeCertificate(newRequestEvent(), responseWriter,
|
||||
makePostRequest(result.FullSerialize()))
|
||||
test.AssertEquals(t, responseWriter.Code, 200)
|
||||
test.AssertEquals(t, responseWriter.Body.String(), "")
|
||||
|
@ -994,7 +995,7 @@ func TestRevokeCertificateWrongKey(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Unable to create revoke request")
|
||||
|
||||
result, _ := accountKeySigner2.Sign(revokeRequestJSON, nonce)
|
||||
wfe.RevokeCertificate(responseWriter,
|
||||
wfe.RevokeCertificate(newRequestEvent(), responseWriter,
|
||||
makePostRequest(result.FullSerialize()))
|
||||
test.AssertEquals(t, responseWriter.Code, 403)
|
||||
test.AssertEquals(t, responseWriter.Body.String(),
|
||||
|
@ -1038,7 +1039,7 @@ func TestRevokeCertificateAlreadyRevoked(t *testing.T) {
|
|||
nonce, err := wfe.nonceService.Nonce()
|
||||
test.AssertNotError(t, err, "Unable to create nonce")
|
||||
result, _ := signer.Sign(revokeRequestJSON, nonce)
|
||||
wfe.RevokeCertificate(responseWriter,
|
||||
wfe.RevokeCertificate(newRequestEvent(), responseWriter,
|
||||
makePostRequest(result.FullSerialize()))
|
||||
test.AssertEquals(t, responseWriter.Code, 409)
|
||||
test.AssertEquals(t, responseWriter.Body.String(),
|
||||
|
@ -1064,7 +1065,7 @@ func TestAuthorization(t *testing.T) {
|
|||
|
||||
// POST, but no body.
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewAuthorization(responseWriter, &http.Request{
|
||||
wfe.NewAuthorization(newRequestEvent(), responseWriter, &http.Request{
|
||||
Method: "POST",
|
||||
Header: map[string][]string{
|
||||
"Content-Length": []string{"0"},
|
||||
|
@ -1074,12 +1075,12 @@ func TestAuthorization(t *testing.T) {
|
|||
|
||||
// POST, but body that isn't valid JWS
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewAuthorization(responseWriter, makePostRequest("hi"))
|
||||
wfe.NewAuthorization(newRequestEvent(), responseWriter, makePostRequest("hi"))
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Parse error reading JWS"}`)
|
||||
|
||||
// POST, Properly JWS-signed, but payload is "foo", not base64-encoded JSON.
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewAuthorization(responseWriter,
|
||||
wfe.NewAuthorization(newRequestEvent(), responseWriter,
|
||||
makePostRequest(signRequest(t, "foo", &wfe.nonceService)))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
|
@ -1088,7 +1089,7 @@ func TestAuthorization(t *testing.T) {
|
|||
// Same signed body, but payload modified by one byte, breaking signature.
|
||||
// should fail JWS verification.
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewAuthorization(responseWriter, makePostRequest(`
|
||||
wfe.NewAuthorization(newRequestEvent(), responseWriter, makePostRequest(`
|
||||
{
|
||||
"header": {
|
||||
"alg": "RS256",
|
||||
|
@ -1107,7 +1108,7 @@ func TestAuthorization(t *testing.T) {
|
|||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: JWS verification error"}`)
|
||||
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewAuthorization(responseWriter,
|
||||
wfe.NewAuthorization(newRequestEvent(), responseWriter,
|
||||
makePostRequest(signRequest(t, `{"resource":"new-authz","identifier":{"type":"dns","value":"test.com"}}`, &wfe.nonceService)))
|
||||
|
||||
test.AssertEquals(
|
||||
|
@ -1166,7 +1167,7 @@ func TestRegistration(t *testing.T) {
|
|||
responseWriter.Body.Reset()
|
||||
|
||||
// Test POST invalid JSON
|
||||
wfe.Registration(responseWriter, makePostRequestWithPath("/2", "invalid"))
|
||||
wfe.Registration(newRequestEvent(), responseWriter, makePostRequestWithPath("/2", "invalid"))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Parse error reading JWS"}`)
|
||||
|
@ -1184,7 +1185,7 @@ func TestRegistration(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Unable to create nonce")
|
||||
result, err := signer.Sign([]byte(`{"resource":"reg","agreement":"`+agreementURL+`"}`), nonce)
|
||||
test.AssertNotError(t, err, "Unable to sign")
|
||||
wfe.Registration(responseWriter,
|
||||
wfe.Registration(newRequestEvent(), responseWriter,
|
||||
makePostRequestWithPath("/2", result.FullSerialize()))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
|
@ -1204,7 +1205,7 @@ func TestRegistration(t *testing.T) {
|
|||
result, err = signer.Sign([]byte(`{"resource":"reg","agreement":"https://letsencrypt.org/im-bad"}`), nonce)
|
||||
|
||||
// Test POST valid JSON with registration up in the mock
|
||||
wfe.Registration(responseWriter,
|
||||
wfe.Registration(newRequestEvent(), responseWriter,
|
||||
makePostRequestWithPath("/1", result.FullSerialize()))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
|
@ -1216,7 +1217,7 @@ func TestRegistration(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Unable to create nonce")
|
||||
result, err = signer.Sign([]byte(`{"resource":"reg","agreement":"`+agreementURL+`"}`), nonce)
|
||||
test.AssertNotError(t, err, "Couldn't sign")
|
||||
wfe.Registration(responseWriter,
|
||||
wfe.Registration(newRequestEvent(), responseWriter,
|
||||
makePostRequestWithPath("/1", result.FullSerialize()))
|
||||
test.AssertNotContains(t, responseWriter.Body.String(), "urn:acme:error")
|
||||
links := responseWriter.Header()["Link"]
|
||||
|
@ -1237,7 +1238,7 @@ func TestTermsRedirect(t *testing.T) {
|
|||
responseWriter := httptest.NewRecorder()
|
||||
|
||||
path, _ := url.Parse("/terms")
|
||||
wfe.Terms(responseWriter, &http.Request{
|
||||
wfe.Terms(newRequestEvent(), responseWriter, &http.Request{
|
||||
Method: "GET",
|
||||
URL: path,
|
||||
})
|
||||
|
@ -1254,7 +1255,7 @@ func TestIssuer(t *testing.T) {
|
|||
|
||||
responseWriter := httptest.NewRecorder()
|
||||
|
||||
wfe.Issuer(responseWriter, &http.Request{
|
||||
wfe.Issuer(newRequestEvent(), responseWriter, &http.Request{
|
||||
Method: "GET",
|
||||
})
|
||||
test.AssertEquals(t, responseWriter.Code, http.StatusOK)
|
||||
|
@ -1264,6 +1265,9 @@ func TestIssuer(t *testing.T) {
|
|||
|
||||
func TestGetCertificate(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
mux, err := wfe.Handler()
|
||||
test.AssertNotError(t, err, "Problem setting up HTTP handlers")
|
||||
|
||||
wfe.CertCacheDuration = time.Second * 10
|
||||
wfe.CertNoCacheExpirationWindow = time.Hour * 24 * 7
|
||||
wfe.SA = &mocks.StorageAuthority{}
|
||||
|
@ -1279,12 +1283,13 @@ func TestGetCertificate(t *testing.T) {
|
|||
// Valid serial, cached
|
||||
req, _ := http.NewRequest("GET", "/acme/cert/0000000000000000000000000000000000b2", nil)
|
||||
req.RemoteAddr = "192.168.0.1"
|
||||
wfe.Certificate(responseWriter, req)
|
||||
mux.ServeHTTP(responseWriter, req)
|
||||
test.AssertEquals(t, responseWriter.Code, 200)
|
||||
test.AssertEquals(t, responseWriter.Header().Get("Cache-Control"), "public, max-age=10")
|
||||
test.AssertEquals(t, responseWriter.Header().Get("Content-Type"), "application/pkix-cert")
|
||||
test.Assert(t, bytes.Compare(responseWriter.Body.Bytes(), certBlock.Bytes) == 0, "Certificates don't match")
|
||||
|
||||
t.Logf("UGH %#v", mockLog.GetAll()[0])
|
||||
reqlogs := mockLog.GetAllMatching(`Successful request`)
|
||||
test.AssertEquals(t, len(reqlogs), 1)
|
||||
test.AssertEquals(t, reqlogs[0].Priority, syslog.LOG_INFO)
|
||||
|
@ -1296,7 +1301,7 @@ func TestGetCertificate(t *testing.T) {
|
|||
req, _ = http.NewRequest("GET", "/acme/cert/0000000000000000000000000000000000ff", nil)
|
||||
req.RemoteAddr = "192.168.0.1"
|
||||
req.Header.Set("X-Forwarded-For", "192.168.99.99")
|
||||
wfe.Certificate(responseWriter, req)
|
||||
mux.ServeHTTP(responseWriter, req)
|
||||
test.AssertEquals(t, responseWriter.Code, 404)
|
||||
test.AssertEquals(t, responseWriter.Header().Get("Cache-Control"), "public, max-age=0, no-cache")
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Certificate not found"}`)
|
||||
|
@ -1309,7 +1314,7 @@ func TestGetCertificate(t *testing.T) {
|
|||
// Invalid serial, no cache
|
||||
responseWriter = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/acme/cert/nothex", nil)
|
||||
wfe.Certificate(responseWriter, req)
|
||||
mux.ServeHTTP(responseWriter, req)
|
||||
test.AssertEquals(t, responseWriter.Code, 404)
|
||||
test.AssertEquals(t, responseWriter.Header().Get("Cache-Control"), "public, max-age=0, no-cache")
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Certificate not found"}`)
|
||||
|
@ -1317,7 +1322,7 @@ func TestGetCertificate(t *testing.T) {
|
|||
// Invalid serial, no cache
|
||||
responseWriter = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/acme/cert/00000000000000", nil)
|
||||
wfe.Certificate(responseWriter, req)
|
||||
mux.ServeHTTP(responseWriter, req)
|
||||
test.AssertEquals(t, responseWriter.Code, 404)
|
||||
test.AssertEquals(t, responseWriter.Header().Get("Cache-Control"), "public, max-age=0, no-cache")
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Certificate not found"}`)
|
||||
|
@ -1359,7 +1364,7 @@ func TestLogCsrPem(t *testing.T) {
|
|||
|
||||
func TestLengthRequired(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
_, _, _, err := wfe.verifyPOST(&http.Request{
|
||||
_, _, _, err := wfe.verifyPOST(newRequestEvent(), &http.Request{
|
||||
Method: "POST",
|
||||
URL: mustParseURL("/"),
|
||||
}, false, "resource")
|
||||
|
@ -1387,7 +1392,7 @@ func TestVerifyPOSTUsesStoredKey(t *testing.T) {
|
|||
wfe.SA = &mockSADifferentStoredKey{mocks.StorageAuthority{}}
|
||||
// signRequest signs with test1Key, but our special mock returns a
|
||||
// registration with test2Key
|
||||
_, _, _, err := wfe.verifyPOST(makePostRequest(signRequest(t, `{"resource":"foo"}`, &wfe.nonceService)), true, "foo")
|
||||
_, _, _, err := wfe.verifyPOST(newRequestEvent(), makePostRequest(signRequest(t, `{"resource":"foo"}`, &wfe.nonceService)), true, "foo")
|
||||
test.AssertError(t, err, "No error returned when provided key differed from stored key.")
|
||||
}
|
||||
|
||||
|
@ -1398,7 +1403,7 @@ func TestBadKeyCSR(t *testing.T) {
|
|||
// CSR with a bad (512 bit RSA) key.
|
||||
// openssl req -outform der -new -newkey rsa:512 -nodes -keyout foo.com.key
|
||||
// -subj /CN=foo.com | base64 -w0 | sed -e 's,+,-,g' -e 's,/,_,g'
|
||||
wfe.NewCertificate(responseWriter,
|
||||
wfe.NewCertificate(newRequestEvent(), responseWriter,
|
||||
makePostRequest(signRequest(t, `{
|
||||
"resource":"new-cert",
|
||||
"csr": "MIHLMHcCAQAwEjEQMA4GA1UEAwwHZm9vLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDCZftp4x4owgjBnwOKfzihIPedT-BUmV2fuQPMqaUlc8yJUp13vcO5uxUlaBm8leM7Dj_sgTDP_JgykorlYo73AgMBAAGgADANBgkqhkiG9w0BAQsFAANBAEaQ2QBhweK-kp1ejQCedUhMit_wG-uTBtKnc3M82f6_fztLkhg1vWQ782nmhbEI5orXp6QtNHgJYnBpqA9Ut00"
|
||||
|
@ -1431,3 +1436,7 @@ func TestStatusCodeFromError(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newRequestEvent() *requestEvent {
|
||||
return &requestEvent{Extra: make(map[string]interface{})}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue