Cors headers (#2021)
This commit is contained in:
parent
43d7547235
commit
0303f51b68
|
@ -0,0 +1,56 @@
|
|||
package api
|
||||
|
||||
func (c *Sys) CORSStatus() (*CORSResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/config/cors")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result CORSResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) ConfigureCORS(req *CORSRequest) (*CORSResponse, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/config/cors")
|
||||
if err := r.SetJSONBody(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result CORSResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) DisableCORS() (*CORSResponse, error) {
|
||||
r := c.c.NewRequest("DELETE", "/v1/sys/config/cors")
|
||||
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result CORSResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
|
||||
}
|
||||
|
||||
type CORSRequest struct {
|
||||
AllowedOrigins string `json:"allowed_origins"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type CORSResponse struct {
|
||||
AllowedOrigins string `json:"allowed_origins"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
|
@ -61,7 +61,6 @@ func Commands(metaPtr *meta.Meta) map[string]cli.CommandFactory {
|
|||
Meta: *metaPtr,
|
||||
}, nil
|
||||
},
|
||||
|
||||
"server": func() (cli.Command, error) {
|
||||
return &command.ServerCommand{
|
||||
Meta: *metaPtr,
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/helper/strutil"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
var preflightHeaders = map[string]string{
|
||||
"Access-Control-Allow-Headers": "*",
|
||||
"Access-Control-Max-Age": "300",
|
||||
}
|
||||
|
||||
var allowedMethods = []string{
|
||||
http.MethodDelete,
|
||||
http.MethodGet,
|
||||
http.MethodOptions,
|
||||
http.MethodPost,
|
||||
http.MethodPut,
|
||||
"LIST", // LIST is not an official HTTP method, but Vault supports it.
|
||||
}
|
||||
|
||||
func wrapCORSHandler(h http.Handler, core *vault.Core) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
corsConf := core.CORSConfig()
|
||||
|
||||
origin := req.Header.Get("Origin")
|
||||
requestMethod := req.Header.Get("Access-Control-Request-Method")
|
||||
|
||||
// If CORS is not enabled or if no Origin header is present (i.e. the request
|
||||
// is from the Vault CLI. A browser will always send an Origin header), then
|
||||
// just return a 204.
|
||||
if !corsConf.IsEnabled() || origin == "" {
|
||||
h.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
// Return a 403 if the origin is not
|
||||
// allowed to make cross-origin requests.
|
||||
if !corsConf.IsValidOrigin(origin) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Method == http.MethodOptions && !strutil.StrListContains(allowedMethods, requestMethod) {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
w.Header().Set("Vary", "Origin")
|
||||
|
||||
// apply headers for preflight requests
|
||||
if req.Method == http.MethodOptions {
|
||||
w.Header().Set("Access-Control-Allow-Methods", strings.Join(allowedMethods, ","))
|
||||
|
||||
for k, v := range preflightHeaders {
|
||||
w.Header().Set(k, v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
h.ServeHTTP(w, req)
|
||||
return
|
||||
})
|
||||
}
|
|
@ -67,10 +67,11 @@ func Handler(core *vault.Core) http.Handler {
|
|||
|
||||
// Wrap the handler in another handler to trigger all help paths.
|
||||
helpWrappedHandler := wrapHelpHandler(mux, core)
|
||||
corsWrappedHandler := wrapCORSHandler(helpWrappedHandler, core)
|
||||
|
||||
// Wrap the help wrapped handler with another layer with a generic
|
||||
// handler
|
||||
genericWrappedHandler := wrapGenericHandler(helpWrappedHandler)
|
||||
genericWrappedHandler := wrapGenericHandler(corsWrappedHandler)
|
||||
|
||||
return genericWrappedHandler
|
||||
}
|
||||
|
|
|
@ -14,6 +14,87 @@ import (
|
|||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
func TestHandler_cors(t *testing.T) {
|
||||
core, _, _ := vault.TestCoreUnsealed(t)
|
||||
ln, addr := TestServer(t, core)
|
||||
defer ln.Close()
|
||||
|
||||
// Enable CORS and allow from any origin for testing.
|
||||
corsConfig := core.CORSConfig()
|
||||
err := corsConfig.Enable([]string{addr})
|
||||
if err != nil {
|
||||
t.Fatalf("Error enabling CORS: %s", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodOptions, addr+"/v1/sys/seal-status", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
req.Header.Set("Origin", "BAD ORIGIN")
|
||||
|
||||
// Requests from unacceptable origins will be rejected with a 403.
|
||||
client := cleanhttp.DefaultClient()
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusForbidden {
|
||||
t.Fatalf("Bad status:\nexpected: 403 Forbidden\nactual: %s", resp.Status)
|
||||
}
|
||||
|
||||
//
|
||||
// Test preflight requests
|
||||
//
|
||||
|
||||
// Set a valid origin
|
||||
req.Header.Set("Origin", addr)
|
||||
|
||||
// Server should NOT accept arbitrary methods.
|
||||
req.Header.Set("Access-Control-Request-Method", "FOO")
|
||||
|
||||
client = cleanhttp.DefaultClient()
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Fail if an arbitrary method is accepted.
|
||||
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||
t.Fatalf("Bad status:\nexpected: 405 Method Not Allowed\nactual: %s", resp.Status)
|
||||
}
|
||||
|
||||
// Server SHOULD accept acceptable methods.
|
||||
req.Header.Set("Access-Control-Request-Method", http.MethodPost)
|
||||
|
||||
client = cleanhttp.DefaultClient()
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
//
|
||||
// Test that the CORS headers are applied correctly.
|
||||
//
|
||||
expHeaders := map[string]string{
|
||||
"Access-Control-Allow-Origin": addr,
|
||||
"Access-Control-Allow-Headers": "*",
|
||||
"Access-Control-Max-Age": "300",
|
||||
"Vary": "Origin",
|
||||
}
|
||||
|
||||
for expHeader, expected := range expHeaders {
|
||||
actual := resp.Header.Get(expHeader)
|
||||
if actual == "" {
|
||||
t.Fatalf("bad:\nHeader: %#v was not on response.", expHeader)
|
||||
}
|
||||
|
||||
if actual != expected {
|
||||
t.Fatalf("bad:\nExpected: %#v\nActual: %#v\n", expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler_CacheControlNoStore(t *testing.T) {
|
||||
core, _, token := vault.TestCoreUnsealed(t)
|
||||
ln, addr := TestServer(t, core)
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -55,6 +56,11 @@ func testHttpData(t *testing.T, method string, token string, addr string, body i
|
|||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Get the address of the local listener in order to attach it to an Origin header.
|
||||
// This will allow for the testing of requests that require CORS, without using a browser.
|
||||
hostURLRegexp, _ := regexp.Compile("http[s]?://.+:[0-9]+")
|
||||
req.Header.Set("Origin", hostURLRegexp.FindString(addr))
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
if len(token) != 0 {
|
||||
|
|
|
@ -49,6 +49,7 @@ func buildLogicalRequest(core *vault.Core, w http.ResponseWriter, r *http.Reques
|
|||
op = logical.UpdateOperation
|
||||
case "LIST":
|
||||
op = logical.ListOperation
|
||||
case "OPTIONS":
|
||||
default:
|
||||
return nil, http.StatusMethodNotAllowed, nil
|
||||
}
|
||||
|
|
|
@ -331,6 +331,9 @@ type Core struct {
|
|||
// The grpc forwarding client
|
||||
rpcForwardingClient *forwardingClient
|
||||
|
||||
// CORS Information
|
||||
corsConfig *CORSConfig
|
||||
|
||||
// replicationState keeps the current replication state cached for quick
|
||||
// lookup
|
||||
replicationState consts.ReplicationState
|
||||
|
@ -447,6 +450,7 @@ func NewCore(conf *CoreConfig) (*Core, error) {
|
|||
clusterName: conf.ClusterName,
|
||||
clusterListenerShutdownCh: make(chan struct{}),
|
||||
clusterListenerShutdownSuccessCh: make(chan struct{}),
|
||||
corsConfig: &CORSConfig{},
|
||||
clusterPeerClusterAddrsCache: cache.New(3*heartbeatInterval, time.Second),
|
||||
enableMlock: !conf.DisableMlock,
|
||||
}
|
||||
|
@ -555,6 +559,11 @@ func (c *Core) Shutdown() error {
|
|||
return c.sealInternal()
|
||||
}
|
||||
|
||||
// CORSConfig returns the current CORS configuration
|
||||
func (c *Core) CORSConfig() *CORSConfig {
|
||||
return c.corsConfig
|
||||
}
|
||||
|
||||
// LookupToken returns the properties of the token from the token store. This
|
||||
// is particularly useful to fetch the accessor of the client token and get it
|
||||
// populated in the logical request along with the client token. The accessor
|
||||
|
@ -1291,6 +1300,9 @@ func (c *Core) postUnseal() (retErr error) {
|
|||
if err := c.setupPolicyStore(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.loadCORSConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.loadCredentials(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1356,6 +1368,9 @@ func (c *Core) preSeal() error {
|
|||
if err := c.teardownPolicyStore(); err != nil {
|
||||
result = multierror.Append(result, errwrap.Wrapf("error tearing down policy store: {{err}}", err))
|
||||
}
|
||||
if err := c.saveCORSConfig(); err != nil {
|
||||
result = multierror.Append(result, errwrap.Wrapf("error tearing down CORS config: {{err}}", err))
|
||||
}
|
||||
if err := c.stopRollback(); err != nil {
|
||||
result = multierror.Append(result, errwrap.Wrapf("error stopping rollback: {{err}}", err))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/vault/helper/strutil"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
var errCORSNotConfigured = errors.New("CORS is not configured")
|
||||
|
||||
// CORSConfig stores the state of the CORS configuration.
|
||||
type CORSConfig struct {
|
||||
sync.RWMutex
|
||||
Enabled bool `json:"enabled"`
|
||||
AllowedOrigins []string `json:"allowed_origins"`
|
||||
}
|
||||
|
||||
func (c *Core) saveCORSConfig() error {
|
||||
view := c.systemBarrierView.SubView("config/")
|
||||
|
||||
entry, err := logical.StorageEntryJSON("cors", c.corsConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create CORS config entry: %v", err)
|
||||
}
|
||||
|
||||
if err := view.Put(entry); err != nil {
|
||||
return fmt.Errorf("failed to save CORS config: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Core) loadCORSConfig() error {
|
||||
view := c.systemBarrierView.SubView("config/")
|
||||
|
||||
// Load the config in
|
||||
out, err := view.Get("cors")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read CORS config: %v", err)
|
||||
}
|
||||
if out == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = out.DecodeJSON(c.corsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Enable takes either a '*' or a comma-seprated list of URLs that can make
|
||||
// cross-origin requests to Vault.
|
||||
func (c *CORSConfig) Enable(urls []string) error {
|
||||
if len(urls) == 0 {
|
||||
return errors.New("the list of allowed origins cannot be empty")
|
||||
}
|
||||
|
||||
if strutil.StrListContains(urls, "*") && len(urls) > 1 {
|
||||
return errors.New("to allow all origins the '*' must be the only value for allowed_origins")
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.AllowedOrigins = urls
|
||||
c.Enabled = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsEnabled returns the value of CORSConfig.isEnabled
|
||||
func (c *CORSConfig) IsEnabled() bool {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
return c.Enabled
|
||||
}
|
||||
|
||||
// Disable sets CORS to disabled and clears the allowed origins
|
||||
func (c *CORSConfig) Disable() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.Enabled = false
|
||||
c.AllowedOrigins = []string{}
|
||||
}
|
||||
|
||||
// IsValidOrigin determines if the origin of the request is allowed to make
|
||||
// cross-origin requests based on the CORSConfig.
|
||||
func (c *CORSConfig) IsValidOrigin(origin string) bool {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
if c.AllowedOrigins == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(c.AllowedOrigins) == 1 && (c.AllowedOrigins)[0] == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
return strutil.StrListContains(c.AllowedOrigins, origin)
|
||||
}
|
|
@ -62,6 +62,7 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
|
|||
"replication/primary/secondary-token",
|
||||
"replication/reindex",
|
||||
"rotate",
|
||||
"config/*",
|
||||
"config/auditing/*",
|
||||
"plugins/catalog/*",
|
||||
"revoke-prefix/*",
|
||||
|
@ -99,6 +100,30 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
|
|||
HelpDescription: strings.TrimSpace(sysHelp["capabilities_accessor"][1]),
|
||||
},
|
||||
|
||||
&framework.Path{
|
||||
Pattern: "config/cors$",
|
||||
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"enable": &framework.FieldSchema{
|
||||
Type: framework.TypeBool,
|
||||
Description: "Enables or disables CORS headers on requests.",
|
||||
},
|
||||
"allowed_origins": &framework.FieldSchema{
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: "A comma-separated list of origins that may make cross-origin requests.",
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.ReadOperation: b.handleCORSRead,
|
||||
logical.UpdateOperation: b.handleCORSUpdate,
|
||||
logical.DeleteOperation: b.handleCORSDelete,
|
||||
},
|
||||
|
||||
HelpDescription: strings.TrimSpace(sysHelp["config/cors"][0]),
|
||||
HelpSynopsis: strings.TrimSpace(sysHelp["config/cors"][1]),
|
||||
},
|
||||
|
||||
&framework.Path{
|
||||
Pattern: "capabilities$",
|
||||
|
||||
|
@ -809,6 +834,41 @@ type SystemBackend struct {
|
|||
Backend *framework.Backend
|
||||
}
|
||||
|
||||
// handleCORSRead returns the current CORS configuration
|
||||
func (b *SystemBackend) handleCORSRead(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
corsConf := b.Core.corsConfig
|
||||
if corsConf == nil {
|
||||
return nil, errCORSNotConfigured
|
||||
}
|
||||
|
||||
return &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"enabled": corsConf.Enabled,
|
||||
"allowed_origins": strings.Join(corsConf.AllowedOrigins, ","),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// handleCORSUpdate sets the list of origins that are allowed
|
||||
// to make cross-origin requests and sets the CORS enabled flag to true
|
||||
func (b *SystemBackend) handleCORSUpdate(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
origins := d.Get("allowed_origins").([]string)
|
||||
|
||||
err := b.Core.corsConfig.Enable(origins)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// handleCORSDelete clears the allowed origins and sets the CORS enabled flag to false
|
||||
func (b *SystemBackend) handleCORSDelete(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
b.Core.CORSConfig().Disable()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *SystemBackend) handleTidyLeases(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
err := b.Core.expiration.Tidy()
|
||||
if err != nil {
|
||||
|
@ -967,7 +1027,7 @@ func (b *SystemBackend) handleAuditedHeadersRead(req *logical.Request, d *framew
|
|||
}, nil
|
||||
}
|
||||
|
||||
// handleCapabilitiesreturns the ACL capabilities of the token for a given path
|
||||
// handleCapabilities returns the ACL capabilities of the token for a given path
|
||||
func (b *SystemBackend) handleCapabilities(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
token := d.Get("token").(string)
|
||||
if token == "" {
|
||||
|
@ -985,8 +1045,8 @@ func (b *SystemBackend) handleCapabilities(req *logical.Request, d *framework.Fi
|
|||
}, nil
|
||||
}
|
||||
|
||||
// handleCapabilitiesAccessor returns the ACL capabilities of the token associted
|
||||
// with the given accessor for a given path.
|
||||
// handleCapabilitiesAccessor returns the ACL capabilities of the
|
||||
// token associted with the given accessor for a given path.
|
||||
func (b *SystemBackend) handleCapabilitiesAccessor(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
accessor := d.Get("accessor").(string)
|
||||
if accessor == "" {
|
||||
|
@ -2244,6 +2304,21 @@ as well as perform core operations.
|
|||
|
||||
// sysHelp is all the help text for the sys backend.
|
||||
var sysHelp = map[string][2]string{
|
||||
"config/cors": {
|
||||
"Configures or returns the current configuration of CORS settings.",
|
||||
`
|
||||
This path responds to the following HTTP methods.
|
||||
|
||||
GET /
|
||||
Returns the configuration of the CORS setting.
|
||||
|
||||
POST /
|
||||
Sets the comma-separated list of origins that can make cross-origin requests.
|
||||
|
||||
DELETE /
|
||||
Clears the CORS configuration and disables acceptance of CORS requests.
|
||||
`,
|
||||
},
|
||||
"init": {
|
||||
"Initializes or returns the initialization status of the Vault.",
|
||||
`
|
||||
|
|
|
@ -31,6 +31,7 @@ func TestSystemBackend_RootPaths(t *testing.T) {
|
|||
"replication/primary/secondary-token",
|
||||
"replication/reindex",
|
||||
"rotate",
|
||||
"config/*",
|
||||
"config/auditing/*",
|
||||
"plugins/catalog/*",
|
||||
"revoke-prefix/*",
|
||||
|
@ -46,6 +47,58 @@ func TestSystemBackend_RootPaths(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSystemConfigCORS(t *testing.T) {
|
||||
b := testSystemBackend(t)
|
||||
|
||||
req := logical.TestRequest(t, logical.UpdateOperation, "config/cors")
|
||||
req.Data["allowed_origins"] = "http://www.example.com"
|
||||
_, err := b.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"enabled": true,
|
||||
"allowed_origins": "http://www.example.com",
|
||||
},
|
||||
}
|
||||
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "config/cors")
|
||||
actual, err := b.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("UPDATE FAILED -- bad: %#v", actual)
|
||||
}
|
||||
|
||||
req = logical.TestRequest(t, logical.DeleteOperation, "config/cors")
|
||||
_, err = b.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "config/cors")
|
||||
actual, err = b.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
expected = &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"enabled": false,
|
||||
"allowed_origins": "",
|
||||
},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("DELETE FAILED -- bad: %#v", actual)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSystemBackend_mounts(t *testing.T) {
|
||||
b := testSystemBackend(t)
|
||||
req := logical.TestRequest(t, logical.ReadOperation, "mounts")
|
||||
|
|
|
@ -156,7 +156,9 @@ The following HTTP status codes are used throughout the API.
|
|||
- `204` - Success, no data returned.
|
||||
- `400` - Invalid request, missing or invalid data.
|
||||
- `403` - Forbidden, your authentication details are either
|
||||
incorrect or you don't have access to this feature.
|
||||
incorrect, you don't have access to this feature, or - if CORS is
|
||||
enabled - you made a cross-origin request from an origin that is
|
||||
not allowed to make such requests.
|
||||
- `404` - Invalid path. This can both mean that the path truly
|
||||
doesn't exist or that you don't have permission to view a
|
||||
specific path. We use 404 in some cases to avoid state leakage.
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
---
|
||||
layout: "http"
|
||||
page_title: "HTTP API: /sys/config/cors"
|
||||
sidebar_current: "docs-http-config-cors"
|
||||
description: |-
|
||||
The '/sys/config/cors' endpoint configures how the Vault server responds to cross-origin requests.
|
||||
---
|
||||
|
||||
# /sys/config/cors
|
||||
|
||||
This is a protected path, therefore all requests require a token with `root`
|
||||
policy or `sudo` capability on the path.
|
||||
|
||||
## GET
|
||||
|
||||
<dl>
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
Returns the current CORS configuration.
|
||||
</dd>
|
||||
|
||||
<dt>Method</dt>
|
||||
<dd>GET</dd>
|
||||
|
||||
<dt>URL</dt>
|
||||
<dd>`/sys/config/cors`</dd>
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
None
|
||||
</dd>
|
||||
|
||||
<dt>Returns</dt>
|
||||
<dd>
|
||||
|
||||
```javascript
|
||||
{
|
||||
"enabled": true,
|
||||
"allowed_origins": "http://www.example.com"
|
||||
}
|
||||
```
|
||||
|
||||
Sample response when CORS is disabled.
|
||||
|
||||
```javascript
|
||||
{
|
||||
"enabled": false,
|
||||
"allowed_origins": ""
|
||||
}
|
||||
```
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
## PUT
|
||||
|
||||
<dl>
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
Configures the Vault server to return CORS headers for origins that are
|
||||
permitted to make cross-origin requests based on the `allowed_origins`
|
||||
parameter.
|
||||
</dd>
|
||||
|
||||
<dt>Method</dt>
|
||||
<dd>PUT</dd>
|
||||
|
||||
<dt>URL</dt>
|
||||
<dd>`/sys/config/cors`</dd>
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">allowed_origins</span>
|
||||
<span class="param-flags">required</span>
|
||||
Valid values are either a wildcard (*) or a comma-separated list of
|
||||
exact origins that are permitted to make cross-origin requests.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt>Returns</dt>
|
||||
<dd>`204` response code.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
## DELETE
|
||||
|
||||
<dl>
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
Disables the CORS functionality of the Vault server.
|
||||
</dd>
|
||||
|
||||
<dt>Method</dt>
|
||||
<dd>DELETE</dd>
|
||||
|
||||
<dt>URL</dt>
|
||||
<dd>`/sys/config/cors`</dd>
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
None
|
||||
</dd>
|
||||
|
||||
<dt>Returns</dt>
|
||||
<dd>`204` response code.
|
||||
</dd>
|
||||
</dl>
|
Loading…
Reference in New Issue