Merge branch 'master' into sig-reuse
This commit is contained in:
commit
31ae51129a
|
@ -83,6 +83,8 @@ func main() {
|
|||
wfe.SA = &sac
|
||||
wfe.SubscriberAgreementURL = c.SubscriberAgreementURL
|
||||
|
||||
wfe.AllowOrigins = c.WFE.AllowOrigins
|
||||
|
||||
wfe.CertCacheDuration, err = time.ParseDuration(c.WFE.CertCacheDuration)
|
||||
cmd.FailOnError(err, "Couldn't parse certificate caching duration")
|
||||
wfe.CertNoCacheExpirationWindow, err = time.ParseDuration(c.WFE.CertNoCacheExpirationWindow)
|
||||
|
|
|
@ -75,6 +75,8 @@ type Config struct {
|
|||
BaseURL string
|
||||
ListenAddress string
|
||||
|
||||
AllowOrigins []string
|
||||
|
||||
CertCacheDuration string
|
||||
CertNoCacheExpirationWindow string
|
||||
IndexCacheDuration string
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
"wfe": {
|
||||
"listenAddress": "127.0.0.1:4000",
|
||||
"allowOrigins": ["*"],
|
||||
"certCacheDuration": "6h",
|
||||
"certNoCacheExpirationWindow": "96h",
|
||||
"indexCacheDuration": "24h",
|
||||
|
|
|
@ -1,161 +0,0 @@
|
|||
{
|
||||
"syslog": {
|
||||
"network": "",
|
||||
"server": "",
|
||||
"tag": "boulder"
|
||||
},
|
||||
|
||||
"amqp": {
|
||||
"server": "amqp://guest:guest@localhost:5672",
|
||||
"RA": {
|
||||
"client": "RA.client",
|
||||
"server": "RA.server"
|
||||
},
|
||||
"VA": {
|
||||
"client": "VA.client",
|
||||
"server": "VA.server"
|
||||
},
|
||||
"SA": {
|
||||
"client": "SA.client",
|
||||
"server": "SA.server"
|
||||
},
|
||||
"CA": {
|
||||
"client": "CA.client",
|
||||
"server": "CA.server"
|
||||
}
|
||||
},
|
||||
|
||||
"statsd": {
|
||||
"server": "localhost:8125",
|
||||
"prefix": "Boulder"
|
||||
},
|
||||
|
||||
"wfe": {
|
||||
"listenAddress": "127.0.0.1:4000",
|
||||
"certCacheDuration": "6h",
|
||||
"certNoCacheExpirationWindow": "96h",
|
||||
"indexCacheDuration": "24h",
|
||||
"issuerCacheDuration": "48h",
|
||||
"debugAddr": "localhost:8000"
|
||||
},
|
||||
|
||||
"ca": {
|
||||
"serialPrefix": 255,
|
||||
"profile": "ee",
|
||||
"dbConnect": "mysql+tcp://boulder@localhost:3306/boulder_test",
|
||||
"debugAddr": "localhost:8001",
|
||||
"testMode": true,
|
||||
"_comment": "This should only be present in testMode. In prod use an HSM.",
|
||||
"expiry": "2160h",
|
||||
"lifespanOCSP": "96h",
|
||||
"maxNames": 1000,
|
||||
"Key": {
|
||||
"PKCS11": {
|
||||
"_comment": "On OS X, the module will likely be /Library/OpenSC/lib/opensc-pkcs11.so",
|
||||
"Module": "/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so",
|
||||
"Token": "Yubico Yubikey NEO OTP+CCID 00 00",
|
||||
"Label": "SIGN key",
|
||||
"PIN": "1234"
|
||||
}
|
||||
},
|
||||
"cfssl": {
|
||||
"signing": {
|
||||
"profiles": {
|
||||
"ee": {
|
||||
"usages": [
|
||||
"digital signature",
|
||||
"key encipherment",
|
||||
"server auth",
|
||||
"client auth"
|
||||
],
|
||||
"backdate": "1h",
|
||||
"is_ca": false,
|
||||
"issuer_urls": [
|
||||
"http://int-x1.letsencrypt.org/cert"
|
||||
],
|
||||
"ocsp_url": "http://int-x1.letsencrypt.org/ocsp",
|
||||
"crl_url": "http://int-x1.letsencrypt.org/crl",
|
||||
"policies": [
|
||||
{
|
||||
"ID": "2.23.140.1.2.1"
|
||||
},
|
||||
],
|
||||
"expiry": "8760h",
|
||||
"CSRWhitelist": {
|
||||
"PublicKeyAlgorithm": true,
|
||||
"PublicKey": true,
|
||||
"SignatureAlgorithm": true
|
||||
},
|
||||
"UseSerialSeq": true
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"usages": [
|
||||
"digital signature"
|
||||
],
|
||||
"expiry": "8760h"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"ra": {
|
||||
"debugAddr": "localhost:8002"
|
||||
},
|
||||
|
||||
"sa": {
|
||||
"dbConnect": "mysql+tcp://boulder@localhost:3306/boulder_test",
|
||||
"debugAddr": "localhost:8003"
|
||||
},
|
||||
|
||||
"va": {
|
||||
"userAgent": "boulder",
|
||||
"debugAddr": "localhost:8004"
|
||||
},
|
||||
|
||||
"sql": {
|
||||
"SQLDebug": true
|
||||
},
|
||||
|
||||
"revoker": {
|
||||
"dbConnect": "mysql+tcp://boulder@localhost:3306/boulder_test"
|
||||
},
|
||||
|
||||
"ocspResponder": {
|
||||
"dbConnect": "mysql+tcp://boulder@localhost:3306/boulder_test",
|
||||
"debugAddr": "localhost:8004",
|
||||
"path": "/",
|
||||
"listenAddress": "localhost:4001"
|
||||
},
|
||||
|
||||
"ocspUpdater": {
|
||||
"dbConnect": "mysql+tcp://boulder@localhost:3306/boulder_test",
|
||||
"debugAddr": "localhost:8005",
|
||||
"minTimeToExpiry": "72h"
|
||||
},
|
||||
|
||||
"activityMonitor": {
|
||||
"debugAddr": "localhost:8006"
|
||||
},
|
||||
|
||||
"mailer": {
|
||||
"server": "mail.example.com",
|
||||
"port": "25",
|
||||
"username": "cert-master@example.com",
|
||||
"password": "password",
|
||||
"dbConnect": "mysql+tcp://boulder@localhost:3306/boulder_test",
|
||||
"messageLimit": 0,
|
||||
"nagTimes": ["24h", "72h", "168h", "336h"],
|
||||
"emailTemplate": "test/example-expiration-template",
|
||||
"debugAddr": "localhost:8004"
|
||||
},
|
||||
|
||||
"common": {
|
||||
"baseURL": "http://localhost:4000",
|
||||
"issuerCert": "test/test-ca.pem",
|
||||
"dnsResolver": "8.8.8.8:53",
|
||||
"dnsTimeout": "10s"
|
||||
},
|
||||
|
||||
"subscriberAgreementURL": "http://localhost:4000/terms"
|
||||
}
|
|
@ -73,7 +73,10 @@ type WebFrontEndImpl struct {
|
|||
IndexCacheDuration time.Duration
|
||||
IssuerCacheDuration time.Duration
|
||||
|
||||
// Gracefull shutdown settings
|
||||
// CORS settings
|
||||
AllowOrigins []string
|
||||
|
||||
// Graceful shutdown settings
|
||||
ShutdownStopTimeout time.Duration
|
||||
ShutdownKillTimeout time.Duration
|
||||
}
|
||||
|
@ -153,17 +156,27 @@ func (mrw BodylessResponseWriter) Write(buf []byte) (int, error) {
|
|||
//
|
||||
// * Set a Replay-Nonce header.
|
||||
//
|
||||
// * Respond to OPTIONS requests, including CORS preflight requests.
|
||||
//
|
||||
// * Respond http.StatusMethodNotAllowed for HTTP methods other than
|
||||
// those listed.
|
||||
// those listed.
|
||||
//
|
||||
// * Set CORS headers when responding to CORS "actual" requests.
|
||||
//
|
||||
// * 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.
|
||||
// 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) {
|
||||
methodsOK := make(map[string]bool)
|
||||
methodsMap := make(map[string]bool)
|
||||
for _, m := range methods {
|
||||
methodsOK[m] = true
|
||||
methodsMap[m] = true
|
||||
}
|
||||
if methodsMap["GET"] && !methodsMap["HEAD"] {
|
||||
// Allow HEAD for any resource that allows GET
|
||||
methods = append(methods, "HEAD")
|
||||
methodsMap["HEAD"] = true
|
||||
}
|
||||
methodsStr := strings.Join(methods, ", ")
|
||||
mux.HandleFunc(pattern, func(response http.ResponseWriter, request *http.Request) {
|
||||
// We do not propagate errors here, because (1) they should be
|
||||
// transient, and (2) they fail closed.
|
||||
|
@ -171,27 +184,29 @@ func (wfe *WebFrontEndImpl) HandleFunc(mux *http.ServeMux, pattern string, h fun
|
|||
if err == nil {
|
||||
response.Header().Set("Replay-Nonce", nonce)
|
||||
}
|
||||
response.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
// Return a bodyless response to HEAD for any resource that allows GET.
|
||||
if _, ok := methodsOK["GET"]; ok && request.Method == "HEAD" {
|
||||
// We'll be sending an error anyway, but we
|
||||
// should still comply with HTTP spec by not
|
||||
switch request.Method {
|
||||
case "HEAD":
|
||||
// Whether or not we're sending a 405 error,
|
||||
// we should comply with HTTP spec by not
|
||||
// sending a body.
|
||||
response = BodylessResponseWriter{response}
|
||||
h(response, request)
|
||||
case "OPTIONS":
|
||||
wfe.Options(response, request, methodsStr, methodsMap)
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := methodsOK[request.Method]; !ok {
|
||||
if !methodsMap[request.Method] {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
logEvent.Error = "Method not allowed"
|
||||
response.Header().Set("Allow", strings.Join(methods, ", "))
|
||||
response.Header().Set("Allow", methodsStr)
|
||||
wfe.sendError(response, logEvent.Error, request.Method, http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
wfe.setCORSHeaders(response, request, "")
|
||||
|
||||
// Call the wrapped handler.
|
||||
h(response, request)
|
||||
})
|
||||
|
@ -885,7 +900,7 @@ func (wfe *WebFrontEndImpl) prepChallengeForDisplay(authz core.Authorization, ch
|
|||
// 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 {
|
||||
for i := range authz.Challenges {
|
||||
wfe.prepChallengeForDisplay(*authz, &authz.Challenges[i])
|
||||
}
|
||||
authz.ID = ""
|
||||
|
@ -1211,6 +1226,61 @@ func (wfe *WebFrontEndImpl) BuildID(response http.ResponseWriter, request *http.
|
|||
}
|
||||
}
|
||||
|
||||
// Options responds to an HTTP OPTIONS request.
|
||||
func (wfe *WebFrontEndImpl) Options(response http.ResponseWriter, request *http.Request, methodsStr string, methodsMap map[string]bool) {
|
||||
// Every OPTIONS request gets an Allow header with a list of supported methods.
|
||||
response.Header().Set("Allow", methodsStr)
|
||||
|
||||
// CORS preflight requests get additional headers. See
|
||||
// http://www.w3.org/TR/cors/#resource-preflight-requests
|
||||
reqMethod := request.Header.Get("Access-Control-Request-Method")
|
||||
if reqMethod == "" {
|
||||
reqMethod = "GET"
|
||||
}
|
||||
if methodsMap[reqMethod] {
|
||||
wfe.setCORSHeaders(response, request, methodsStr)
|
||||
}
|
||||
}
|
||||
|
||||
// setCORSHeaders() tells the client that CORS is acceptable for this
|
||||
// request. If allowMethods == "" the request is assumed to be a CORS
|
||||
// actual request and no Access-Control-Allow-Methods header will be
|
||||
// sent.
|
||||
func (wfe *WebFrontEndImpl) setCORSHeaders(response http.ResponseWriter, request *http.Request, allowMethods string) {
|
||||
reqOrigin := request.Header.Get("Origin")
|
||||
if reqOrigin == "" {
|
||||
// This is not a CORS request.
|
||||
return
|
||||
}
|
||||
|
||||
// Allow CORS if the current origin (or "*") is listed as an
|
||||
// allowed origin in config. Otherwise, disallow by returning
|
||||
// without setting any CORS headers.
|
||||
allow := false
|
||||
for _, ao := range wfe.AllowOrigins {
|
||||
if ao == "*" {
|
||||
response.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
allow = true
|
||||
break
|
||||
} else if ao == reqOrigin {
|
||||
response.Header().Set("Vary", "Origin")
|
||||
response.Header().Set("Access-Control-Allow-Origin", ao)
|
||||
allow = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !allow {
|
||||
return
|
||||
}
|
||||
|
||||
if allowMethods != "" {
|
||||
// For an OPTIONS request: allow all methods handled at this URL.
|
||||
response.Header().Set("Access-Control-Allow-Methods", allowMethods)
|
||||
}
|
||||
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
|
||||
|
|
|
@ -252,6 +252,15 @@ func sortHeader(s string) string {
|
|||
return strings.Join(a, ", ")
|
||||
}
|
||||
|
||||
func addHeadIfGet(s []string) []string {
|
||||
for _, a := range s {
|
||||
if a == "GET" {
|
||||
return append(s, "HEAD")
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func TestHandleFunc(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
var mux *http.ServeMux
|
||||
|
@ -270,32 +279,34 @@ func TestHandleFunc(t *testing.T) {
|
|||
|
||||
// Plain requests (no CORS)
|
||||
type testCase struct {
|
||||
allowed []string
|
||||
reqMethod string
|
||||
shouldSucceed bool
|
||||
allowed []string
|
||||
reqMethod string
|
||||
shouldCallStub bool
|
||||
shouldSucceed bool
|
||||
}
|
||||
var lastNonce string
|
||||
for _, c := range []testCase{
|
||||
{[]string{"GET", "POST"}, "GET", true},
|
||||
{[]string{"GET", "POST"}, "POST", true},
|
||||
{[]string{"GET"}, "", false},
|
||||
{[]string{"GET"}, "POST", false},
|
||||
{[]string{"GET"}, "OPTIONS", false}, // TODO, #469
|
||||
{[]string{"GET"}, "MAKE-COFFEE", false}, // 405, or 418?
|
||||
{[]string{"GET", "POST"}, "GET", true, true},
|
||||
{[]string{"GET", "POST"}, "POST", true, true},
|
||||
{[]string{"GET"}, "", false, false},
|
||||
{[]string{"GET"}, "POST", false, false},
|
||||
{[]string{"GET"}, "OPTIONS", false, true},
|
||||
{[]string{"GET"}, "MAKE-COFFEE", false, false}, // 405, or 418?
|
||||
} {
|
||||
runWrappedHandler(&http.Request{Method: c.reqMethod}, c.allowed...)
|
||||
test.AssertEquals(t, stubCalled, c.shouldSucceed)
|
||||
test.AssertEquals(t, stubCalled, c.shouldCallStub)
|
||||
if c.shouldSucceed {
|
||||
test.AssertEquals(t, rw.Code, http.StatusOK)
|
||||
} else {
|
||||
test.AssertEquals(t, rw.Code, http.StatusMethodNotAllowed)
|
||||
test.AssertEquals(t, sortHeader(rw.Header().Get("Allow")), strings.Join(c.allowed, ", "))
|
||||
test.AssertEquals(t, sortHeader(rw.Header().Get("Allow")), sortHeader(strings.Join(addHeadIfGet(c.allowed), ", ")))
|
||||
test.AssertEquals(t,
|
||||
rw.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Method not allowed"}`)
|
||||
}
|
||||
nonce := rw.Header().Get("Replay-Nonce")
|
||||
test.AssertNotEquals(t, nonce, lastNonce)
|
||||
test.AssertNotEquals(t, nonce, "")
|
||||
lastNonce = nonce
|
||||
}
|
||||
|
||||
|
@ -303,7 +314,7 @@ func TestHandleFunc(t *testing.T) {
|
|||
runWrappedHandler(&http.Request{Method: "PUT"}, "GET", "POST")
|
||||
test.AssertEquals(t, rw.Header().Get("Content-Type"), "application/problem+json")
|
||||
test.AssertEquals(t, rw.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Method not allowed"}`)
|
||||
test.AssertEquals(t, sortHeader(rw.Header().Get("Allow")), "GET, POST")
|
||||
test.AssertEquals(t, sortHeader(rw.Header().Get("Allow")), "GET, HEAD, POST")
|
||||
|
||||
// Disallowed method special case: response to HEAD has got no body
|
||||
runWrappedHandler(&http.Request{Method: "HEAD"}, "GET", "POST")
|
||||
|
@ -313,43 +324,137 @@ func TestHandleFunc(t *testing.T) {
|
|||
// HEAD doesn't work with POST-only endpoints
|
||||
runWrappedHandler(&http.Request{Method: "HEAD"}, "POST")
|
||||
test.AssertEquals(t, stubCalled, false)
|
||||
test.AssertEquals(t, rw.Code, http.StatusMethodNotAllowed)
|
||||
test.AssertEquals(t, rw.Header().Get("Content-Type"), "application/problem+json")
|
||||
test.AssertEquals(t, rw.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Method not allowed"}`)
|
||||
test.AssertEquals(t, rw.Header().Get("Allow"), "POST")
|
||||
}
|
||||
test.AssertEquals(t, rw.Body.String(), "")
|
||||
|
||||
func TestStandardHeaders(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
mux, err := wfe.Handler()
|
||||
test.AssertNotError(t, err, "Problem setting up HTTP handlers")
|
||||
wfe.AllowOrigins = []string{"*"}
|
||||
testOrigin := "https://example.com"
|
||||
|
||||
cases := []struct {
|
||||
path string
|
||||
allowed []string
|
||||
}{
|
||||
{wfe.NewReg, []string{"POST"}},
|
||||
{wfe.RegBase, []string{"POST"}},
|
||||
{wfe.NewAuthz, []string{"POST"}},
|
||||
{wfe.AuthzBase, []string{"GET"}},
|
||||
{wfe.ChallengeBase, []string{"GET", "POST"}},
|
||||
{wfe.NewCert, []string{"POST"}},
|
||||
{wfe.CertBase, []string{"GET"}},
|
||||
{wfe.SubscriberAgreementURL, []string{"GET"}},
|
||||
// CORS "actual" request for disallowed method
|
||||
runWrappedHandler(&http.Request{
|
||||
Method: "POST",
|
||||
Header: map[string][]string{
|
||||
"Origin": {testOrigin},
|
||||
},
|
||||
}, "GET")
|
||||
test.AssertEquals(t, stubCalled, false)
|
||||
test.AssertEquals(t, rw.Code, http.StatusMethodNotAllowed)
|
||||
|
||||
// CORS "actual" request for allowed method
|
||||
runWrappedHandler(&http.Request{
|
||||
Method: "GET",
|
||||
Header: map[string][]string{
|
||||
"Origin": {testOrigin},
|
||||
},
|
||||
}, "GET", "POST")
|
||||
test.AssertEquals(t, stubCalled, true)
|
||||
test.AssertEquals(t, rw.Code, http.StatusOK)
|
||||
test.AssertEquals(t, rw.Header().Get("Access-Control-Allow-Methods"), "")
|
||||
test.AssertEquals(t, rw.Header().Get("Access-Control-Allow-Origin"), "*")
|
||||
test.AssertEquals(t, sortHeader(rw.Header().Get("Access-Control-Expose-Headers")), "Link, Replay-Nonce")
|
||||
|
||||
// CORS preflight request for disallowed method
|
||||
runWrappedHandler(&http.Request{
|
||||
Method: "OPTIONS",
|
||||
Header: map[string][]string{
|
||||
"Origin": {testOrigin},
|
||||
"Access-Control-Request-Method": {"POST"},
|
||||
},
|
||||
}, "GET")
|
||||
test.AssertEquals(t, stubCalled, false)
|
||||
test.AssertEquals(t, rw.Code, http.StatusOK)
|
||||
test.AssertEquals(t, rw.Header().Get("Allow"), "GET, HEAD")
|
||||
test.AssertEquals(t, rw.Header().Get("Access-Control-Allow-Origin"), "")
|
||||
|
||||
// CORS preflight request for allowed method
|
||||
runWrappedHandler(&http.Request{
|
||||
Method: "OPTIONS",
|
||||
Header: map[string][]string{
|
||||
"Origin": {testOrigin},
|
||||
"Access-Control-Request-Method": {"POST"},
|
||||
"Access-Control-Request-Headers": {"X-Accept-Header1, X-Accept-Header2", "X-Accept-Header3"},
|
||||
},
|
||||
}, "GET", "POST")
|
||||
test.AssertEquals(t, rw.Code, http.StatusOK)
|
||||
test.AssertEquals(t, rw.Header().Get("Access-Control-Allow-Origin"), "*")
|
||||
test.AssertEquals(t, rw.Header().Get("Access-Control-Max-Age"), "86400")
|
||||
test.AssertEquals(t, sortHeader(rw.Header().Get("Access-Control-Allow-Methods")), "GET, HEAD, POST")
|
||||
test.AssertEquals(t, sortHeader(rw.Header().Get("Access-Control-Expose-Headers")), "Link, Replay-Nonce")
|
||||
|
||||
// OPTIONS request without an Origin header (i.e., not a CORS
|
||||
// preflight request)
|
||||
runWrappedHandler(&http.Request{
|
||||
Method: "OPTIONS",
|
||||
Header: map[string][]string{
|
||||
"Access-Control-Request-Method": {"POST"},
|
||||
},
|
||||
}, "GET", "POST")
|
||||
test.AssertEquals(t, rw.Code, http.StatusOK)
|
||||
test.AssertEquals(t, rw.Header().Get("Access-Control-Allow-Origin"), "")
|
||||
test.AssertEquals(t, sortHeader(rw.Header().Get("Allow")), "GET, HEAD, POST")
|
||||
|
||||
// CORS preflight request missing optional Request-Method
|
||||
// header. The "actual" request will be GET.
|
||||
for _, allowedMethod := range []string{"GET", "POST"} {
|
||||
runWrappedHandler(&http.Request{
|
||||
Method: "OPTIONS",
|
||||
Header: map[string][]string{
|
||||
"Origin": {testOrigin},
|
||||
},
|
||||
}, allowedMethod)
|
||||
test.AssertEquals(t, rw.Code, http.StatusOK)
|
||||
if allowedMethod == "GET" {
|
||||
test.AssertEquals(t, rw.Header().Get("Access-Control-Allow-Origin"), "*")
|
||||
test.AssertEquals(t, rw.Header().Get("Access-Control-Allow-Methods"), "GET, HEAD")
|
||||
} else {
|
||||
test.AssertEquals(t, rw.Header().Get("Access-Control-Allow-Origin"), "")
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
responseWriter := httptest.NewRecorder()
|
||||
mux.ServeHTTP(responseWriter, &http.Request{
|
||||
Method: "BOGUS",
|
||||
URL: mustParseURL(c.path),
|
||||
})
|
||||
acao := responseWriter.Header().Get("Access-Control-Allow-Origin")
|
||||
nonce := responseWriter.Header().Get("Replay-Nonce")
|
||||
allow := responseWriter.Header().Get("Allow")
|
||||
test.Assert(t, responseWriter.Code == http.StatusMethodNotAllowed, "Bogus method allowed")
|
||||
test.Assert(t, acao == "*", "Bad CORS header")
|
||||
test.Assert(t, len(nonce) > 0, "Bad Replay-Nonce header")
|
||||
test.Assert(t, len(allow) > 0 && allow == strings.Join(c.allowed, ", "), "Bad Allow header")
|
||||
// No CORS headers are given when configuration does not list
|
||||
// "*" or the client-provided origin.
|
||||
for _, wfe.AllowOrigins = range [][]string{
|
||||
{},
|
||||
{"http://example.com", "https://other.example"},
|
||||
{""}, // Invalid origin is never matched
|
||||
} {
|
||||
runWrappedHandler(&http.Request{
|
||||
Method: "OPTIONS",
|
||||
Header: map[string][]string{
|
||||
"Origin": {testOrigin},
|
||||
"Access-Control-Request-Method": {"POST"},
|
||||
},
|
||||
}, "POST")
|
||||
test.AssertEquals(t, rw.Code, http.StatusOK)
|
||||
for _, h := range []string{
|
||||
"Access-Control-Allow-Methods",
|
||||
"Access-Control-Allow-Origin",
|
||||
"Access-Control-Expose-Headers",
|
||||
"Access-Control-Request-Headers",
|
||||
} {
|
||||
test.AssertEquals(t, rw.Header().Get(h), "")
|
||||
}
|
||||
}
|
||||
|
||||
// CORS headers are offered when configuration lists "*" or
|
||||
// the client-provided origin.
|
||||
for _, wfe.AllowOrigins = range [][]string{
|
||||
{testOrigin, "http://example.org", "*"},
|
||||
{"", "http://example.org", testOrigin}, // Invalid origin is harmless
|
||||
} {
|
||||
runWrappedHandler(&http.Request{
|
||||
Method: "OPTIONS",
|
||||
Header: map[string][]string{
|
||||
"Origin": {testOrigin},
|
||||
"Access-Control-Request-Method": {"POST"},
|
||||
},
|
||||
}, "POST")
|
||||
test.AssertEquals(t, rw.Code, http.StatusOK)
|
||||
test.AssertEquals(t, rw.Header().Get("Access-Control-Allow-Origin"), testOrigin)
|
||||
// http://www.w3.org/TR/cors/ section 6.4:
|
||||
test.AssertEquals(t, rw.Header().Get("Vary"), "Origin")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1182,12 +1287,12 @@ func assertCsrLogged(t *testing.T, mockLog *mocks.MockSyslogWriter) {
|
|||
}
|
||||
|
||||
func TestLogCsrPem(t *testing.T) {
|
||||
const certificateRequestJson = `{
|
||||
const certificateRequestJSON = `{
|
||||
"csr": "MIICWTCCAUECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAycX3ca-fViOuRWF38mssORISFxbJvspDfhPGRBZDxJ63NIqQzupB-6dp48xkcX7Z_KDaRJStcpJT2S0u33moNT4FHLklQBETLhExDk66cmlz6Xibp3LGZAwhWuec7wJoEwIgY8oq4rxihIyGq7HVIJoq9DqZGrUgfZMDeEJqbphukQOaXGEop7mD-eeu8-z5EVkB1LiJ6Yej6R8MAhVPHzG5fyOu6YVo6vY6QgwjRLfZHNj5XthxgPIEETZlUbiSoI6J19GYHvLURBTy5Ys54lYAPIGfNwcIBAH4gtH9FrYcDY68R22rp4iuxdvkf03ZWiT0F2W1y7_C9B2jayTzvQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAHd6Do9DIZ2hvdt1GwBXYjsqprZidT_DYOMfYcK17KlvdkFT58XrBH88ulLZ72NXEpiFMeTyzfs3XEyGq_Bbe7TBGVYZabUEh-LOskYwhgcOuThVN7tHnH5rhN-gb7cEdysjTb1QL-vOUwYgV75CB6PE5JVYK-cQsMIVvo0Kz4TpNgjJnWzbcH7h0mtvub-fCv92vBPjvYq8gUDLNrok6rbg05tdOJkXsF2G_W-Q6sf2Fvx0bK5JeH4an7P7cXF9VG9nd4sRt5zd-L3IcyvHVKxNhIJXZVH0AOqh_1YrKI9R0QKQiZCEy0xN1okPlcaIVaFhb7IKAHPxTI3r5f72LXY"
|
||||
}`
|
||||
wfe := setupWFE(t)
|
||||
var certificateRequest core.CertificateRequest
|
||||
err := json.Unmarshal([]byte(certificateRequestJson), &certificateRequest)
|
||||
err := json.Unmarshal([]byte(certificateRequestJSON), &certificateRequest)
|
||||
test.AssertNotError(t, err, "Unable to parse certificateRequest")
|
||||
|
||||
mockSA := mocks.MockSA{}
|
||||
|
|
Loading…
Reference in New Issue