VAULT-12112: add openapi response structures for /sys/config and /sys/generate-root endpoints (#18472)
* some config responses Signed-off-by: Daniel Huckins <dhuckins@users.noreply.github.com> * added response structs Signed-off-by: Daniel Huckins <dhuckins@users.noreply.github.com> * added changelog * add test for config/cors Signed-off-by: Daniel Huckins <dhuckins@users.noreply.github.com> * add (failing) tests Signed-off-by: Daniel Huckins <dhuckins@users.noreply.github.com> * copy-pasta err Signed-off-by: Daniel Huckins <dhuckins@users.noreply.github.com> * update tests for /sys/config/ui/headers/{header} Signed-off-by: Daniel Huckins <dhuckins@users.noreply.github.com> --------- Signed-off-by: Daniel Huckins <dhuckins@users.noreply.github.com>
This commit is contained in:
parent
60488687ad
commit
448f5dd33e
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
openapi: add openapi response defintions to /sys/config and /sys/generate-root endpoints
|
||||
```
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/hashicorp/vault/helper/namespace"
|
||||
"github.com/hashicorp/vault/internalshared/configutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/logging"
|
||||
"github.com/hashicorp/vault/sdk/helper/testhelpers/schema"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/hashicorp/vault/sdk/physical/inmem"
|
||||
)
|
||||
|
@ -98,6 +99,7 @@ func TestConfigCustomHeaders(t *testing.T) {
|
|||
|
||||
func TestCustomResponseHeadersConfigInteractUiConfig(t *testing.T) {
|
||||
b := testSystemBackend(t)
|
||||
paths := b.(*SystemBackend).configPaths()
|
||||
_, barrier, _ := mockBarrier(t)
|
||||
view := NewBarrierView(barrier, "")
|
||||
b.(*SystemBackend).Core.systemBarrierView = view
|
||||
|
@ -143,6 +145,13 @@ func TestCustomResponseHeadersConfigInteractUiConfig(t *testing.T) {
|
|||
if err == nil {
|
||||
t.Fatal("request did not fail on setting a header that is present in custom response headers")
|
||||
}
|
||||
schema.ValidateResponse(
|
||||
t,
|
||||
schema.FindResponseSchema(t, paths, 3, req.Operation),
|
||||
resp,
|
||||
true,
|
||||
)
|
||||
|
||||
if !strings.Contains(resp.Data["error"].(string), fmt.Sprintf("This header already exists in the server configuration and cannot be set in the UI.")) {
|
||||
t.Fatalf("failed to get the expected error")
|
||||
}
|
||||
|
@ -152,10 +161,17 @@ func TestCustomResponseHeadersConfigInteractUiConfig(t *testing.T) {
|
|||
req.Data["values"] = []string{"400"}
|
||||
req.ResponseWriter = hw
|
||||
|
||||
_, err = b.HandleRequest(namespace.RootContext(nil), req)
|
||||
resp, err = b.HandleRequest(namespace.RootContext(nil), req)
|
||||
if err == nil {
|
||||
t.Fatal("request did not fail on setting a header that is present in custom response headers")
|
||||
}
|
||||
schema.ValidateResponse(
|
||||
t,
|
||||
schema.FindResponseSchema(t, paths, 3, req.Operation),
|
||||
resp,
|
||||
true,
|
||||
)
|
||||
|
||||
h, err := b.(*SystemBackend).Core.uiConfig.Headers(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -169,10 +185,16 @@ func TestCustomResponseHeadersConfigInteractUiConfig(t *testing.T) {
|
|||
req.Data["values"] = []string{"Ui header value"}
|
||||
req.ResponseWriter = hw
|
||||
|
||||
_, err = b.HandleRequest(namespace.RootContext(nil), req)
|
||||
resp, err = b.HandleRequest(namespace.RootContext(nil), req)
|
||||
if err != nil {
|
||||
t.Fatal("request failed on setting a header that is not present in custom response headers.", "error:", err)
|
||||
}
|
||||
schema.ValidateResponse(
|
||||
t,
|
||||
schema.FindResponseSchema(t, paths, 3, req.Operation),
|
||||
resp,
|
||||
true,
|
||||
)
|
||||
|
||||
h, err = b.(*SystemBackend).Core.uiConfig.Headers(context.Background())
|
||||
if err != nil {
|
||||
|
|
|
@ -33,15 +33,44 @@ func (b *SystemBackend) configPaths() []*framework.Path {
|
|||
Callback: b.handleCORSRead,
|
||||
Summary: "Return the current CORS settings.",
|
||||
Description: "",
|
||||
Responses: map[int][]framework.Response{
|
||||
http.StatusOK: {{
|
||||
Description: "OK",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"enabled": {
|
||||
Type: framework.TypeBool,
|
||||
Required: true,
|
||||
},
|
||||
"allowed_origins": {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Required: false,
|
||||
},
|
||||
"allowed_headers": {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Required: false,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
logical.UpdateOperation: &framework.PathOperation{
|
||||
Callback: b.handleCORSUpdate,
|
||||
Summary: "Configure the CORS settings.",
|
||||
Description: "",
|
||||
Responses: map[int][]framework.Response{
|
||||
http.StatusNoContent: {{
|
||||
Description: "OK",
|
||||
}},
|
||||
},
|
||||
},
|
||||
logical.DeleteOperation: &framework.PathOperation{
|
||||
Callback: b.handleCORSDelete,
|
||||
Summary: "Remove any CORS settings.",
|
||||
Responses: map[int][]framework.Response{
|
||||
http.StatusNoContent: {{
|
||||
Description: "OK",
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -56,6 +85,13 @@ func (b *SystemBackend) configPaths() []*framework.Path {
|
|||
Callback: b.handleConfigStateSanitized,
|
||||
Summary: "Return a sanitized version of the Vault server configuration.",
|
||||
Description: "The sanitized output strips configuration values in the storage, HA storage, and seals stanzas, which may contain sensitive values such as API tokens. It also removes any token or secret fields in other stanzas, such as the circonus_api_token from telemetry.",
|
||||
Responses: map[int][]framework.Response{
|
||||
http.StatusOK: {{
|
||||
Description: "OK",
|
||||
// response has dynamic keys
|
||||
Fields: map[string]*framework.FieldSchema{},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -73,6 +109,11 @@ func (b *SystemBackend) configPaths() []*framework.Path {
|
|||
Callback: b.handleConfigReload,
|
||||
Summary: "Reload the given subsystem",
|
||||
Description: "",
|
||||
Responses: map[int][]framework.Response{
|
||||
http.StatusNoContent: {{
|
||||
Description: "OK",
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -99,14 +140,42 @@ func (b *SystemBackend) configPaths() []*framework.Path {
|
|||
logical.ReadOperation: &framework.PathOperation{
|
||||
Callback: b.handleConfigUIHeadersRead,
|
||||
Summary: "Return the given UI header's configuration",
|
||||
Responses: map[int][]framework.Response{
|
||||
http.StatusOK: {{
|
||||
Description: "OK",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"value": {
|
||||
Type: framework.TypeString,
|
||||
Required: false,
|
||||
Description: "returns the first header value when `multivalue` request parameter is false",
|
||||
},
|
||||
"values": {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Required: false,
|
||||
Description: "returns all header values when `multivalue` request parameter is true",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
logical.UpdateOperation: &framework.PathOperation{
|
||||
Callback: b.handleConfigUIHeadersUpdate,
|
||||
Summary: "Configure the values to be returned for the UI header.",
|
||||
Responses: map[int][]framework.Response{
|
||||
http.StatusOK: {{
|
||||
// returns 200 with null `data`
|
||||
Description: "OK",
|
||||
}},
|
||||
},
|
||||
},
|
||||
logical.DeleteOperation: &framework.PathOperation{
|
||||
Callback: b.handleConfigUIHeadersDelete,
|
||||
Summary: "Remove a UI header.",
|
||||
Responses: map[int][]framework.Response{
|
||||
http.StatusNoContent: {{
|
||||
Description: "OK",
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -121,6 +190,17 @@ func (b *SystemBackend) configPaths() []*framework.Path {
|
|||
logical.ListOperation: &framework.PathOperation{
|
||||
Callback: b.handleConfigUIHeadersList,
|
||||
Summary: "Return a list of configured UI headers.",
|
||||
Responses: map[int][]framework.Response{
|
||||
http.StatusOK: {{
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"keys": {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: "Lists of configured UI headers. Omitted if list is empty",
|
||||
Required: false,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -139,13 +219,112 @@ func (b *SystemBackend) configPaths() []*framework.Path {
|
|||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.ReadOperation: &framework.PathOperation{
|
||||
Summary: "Read the configuration and progress of the current root generation attempt.",
|
||||
Responses: map[int][]framework.Response{
|
||||
http.StatusOK: {{
|
||||
Description: "OK",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"nonce": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"started": {
|
||||
Type: framework.TypeBool,
|
||||
Required: true,
|
||||
},
|
||||
"progress": {
|
||||
Type: framework.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
"required": {
|
||||
Type: framework.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
"complete": {
|
||||
Type: framework.TypeBool,
|
||||
Required: true,
|
||||
},
|
||||
"encoded_token": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"encoded_root_token": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"pgp_fingerprint": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"otp": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"otp_length": {
|
||||
Type: framework.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
logical.UpdateOperation: &framework.PathOperation{
|
||||
Summary: "Initializes a new root generation attempt.",
|
||||
Description: "Only a single root generation attempt can take place at a time. One (and only one) of otp or pgp_key are required.",
|
||||
Responses: map[int][]framework.Response{
|
||||
http.StatusOK: {{
|
||||
Description: "OK",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"nonce": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"started": {
|
||||
Type: framework.TypeBool,
|
||||
Required: true,
|
||||
},
|
||||
"progress": {
|
||||
Type: framework.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
"required": {
|
||||
Type: framework.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
"complete": {
|
||||
Type: framework.TypeBool,
|
||||
Required: true,
|
||||
},
|
||||
"encoded_token": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"encoded_root_token": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"pgp_fingerprint": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"otp": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"otp_length": {
|
||||
Type: framework.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
logical.DeleteOperation: &framework.PathOperation{
|
||||
Summary: "Cancels any in-progress root generation attempt.",
|
||||
Responses: map[int][]framework.Response{
|
||||
http.StatusNoContent: {{
|
||||
Description: "OK",
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -168,6 +347,53 @@ func (b *SystemBackend) configPaths() []*framework.Path {
|
|||
logical.UpdateOperation: &framework.PathOperation{
|
||||
Summary: "Enter a single unseal key share to progress the root generation attempt.",
|
||||
Description: "If the threshold number of unseal key shares is reached, Vault will complete the root generation and issue the new token. Otherwise, this API must be called multiple times until that threshold is met. The attempt nonce must be provided with each call.",
|
||||
Responses: map[int][]framework.Response{
|
||||
http.StatusOK: {{
|
||||
Description: "OK",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"nonce": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"started": {
|
||||
Type: framework.TypeBool,
|
||||
Required: true,
|
||||
},
|
||||
"progress": {
|
||||
Type: framework.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
"required": {
|
||||
Type: framework.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
"complete": {
|
||||
Type: framework.TypeBool,
|
||||
Required: true,
|
||||
},
|
||||
"encoded_token": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"encoded_root_token": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"pgp_fingerprint": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"otp": {
|
||||
Type: framework.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"otp_length": {
|
||||
Type: framework.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ func TestSystemBackend_RootPaths(t *testing.T) {
|
|||
|
||||
func TestSystemConfigCORS(t *testing.T) {
|
||||
b := testSystemBackend(t)
|
||||
paths := b.(*SystemBackend).configPaths()
|
||||
_, barrier, _ := mockBarrier(t)
|
||||
view := NewBarrierView(barrier, "")
|
||||
b.(*SystemBackend).Core.systemBarrierView = view
|
||||
|
@ -104,37 +105,67 @@ func TestSystemConfigCORS(t *testing.T) {
|
|||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
schema.ValidateResponse(
|
||||
t,
|
||||
schema.FindResponseSchema(t, paths, 0, req.Operation),
|
||||
actual,
|
||||
true,
|
||||
)
|
||||
|
||||
// Do it again. Bug #6182
|
||||
req = logical.TestRequest(t, logical.UpdateOperation, "config/cors")
|
||||
req.Data["allowed_origins"] = "http://www.example.com"
|
||||
req.Data["allowed_headers"] = "X-Custom-Header"
|
||||
_, err = b.HandleRequest(namespace.RootContext(nil), req)
|
||||
resp, err := b.HandleRequest(namespace.RootContext(nil), req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
schema.ValidateResponse(
|
||||
t,
|
||||
schema.FindResponseSchema(t, paths, 0, req.Operation),
|
||||
resp,
|
||||
true,
|
||||
)
|
||||
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "config/cors")
|
||||
actual, err = b.HandleRequest(namespace.RootContext(nil), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
schema.ValidateResponse(
|
||||
t,
|
||||
schema.FindResponseSchema(t, paths, 0, req.Operation),
|
||||
actual,
|
||||
true,
|
||||
)
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
|
||||
req = logical.TestRequest(t, logical.DeleteOperation, "config/cors")
|
||||
_, err = b.HandleRequest(namespace.RootContext(nil), req)
|
||||
resp, err = b.HandleRequest(namespace.RootContext(nil), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
schema.ValidateResponse(
|
||||
t,
|
||||
schema.FindResponseSchema(t, paths, 0, req.Operation),
|
||||
resp,
|
||||
true,
|
||||
)
|
||||
|
||||
req = logical.TestRequest(t, logical.ReadOperation, "config/cors")
|
||||
actual, err = b.HandleRequest(namespace.RootContext(nil), req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
schema.ValidateResponse(
|
||||
t,
|
||||
schema.FindResponseSchema(t, paths, 0, req.Operation),
|
||||
actual,
|
||||
true,
|
||||
)
|
||||
|
||||
expected = &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
|
|
Loading…
Reference in New Issue