added OpenAPI response objects for sys endpoints (#18633)

* added response objects for sys 3 section

* Update vault/logical_system_paths.go

Co-authored-by: Daniel Huckins <dhuckins@users.noreply.github.com>

* Update vault/logical_raw.go

Co-authored-by: Daniel Huckins <dhuckins@users.noreply.github.com>

* Update vault/logical_system_paths.go

Co-authored-by: Daniel Huckins <dhuckins@users.noreply.github.com>

* Update vault/logical_system_quotas.go

Co-authored-by: Daniel Huckins <dhuckins@users.noreply.github.com>

* Update vault/logical_system_quotas.go

Co-authored-by: Daniel Huckins <dhuckins@users.noreply.github.com>

* Update vault/logical_system_quotas.go

Co-authored-by: Daniel Huckins <dhuckins@users.noreply.github.com>

* add tests and update based on reviews

* added changelog file

* finally got make fmt to work...

* fixed copy pasta test case

* updated based on review

* Update vault/logical_system_quotas.go

Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com>

* Update vault/logical_system_test.go

Co-authored-by: Daniel Huckins <dhuckins@users.noreply.github.com>

* Update vault/logical_system_test.go

Co-authored-by: Daniel Huckins <dhuckins@users.noreply.github.com>

---------

Co-authored-by: lursu <leland.ursu@hashicorp.com>
Co-authored-by: Daniel Huckins <dhuckins@users.noreply.github.com>
Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com>
This commit is contained in:
Leland Ursu 2023-02-15 15:02:21 -05:00 committed by GitHub
parent 6425130605
commit 0704127020
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 404 additions and 12 deletions

3
changelog/18633.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
openapi: Add openapi response definitions to /sys defined endpoints.
```

View File

@ -5,6 +5,7 @@ import (
"context"
"encoding/base64"
"fmt"
"net/http"
"strings"
log "github.com/hashicorp/go-hclog"
@ -315,23 +316,60 @@ func rawPaths(prefix string, r *RawBackend) []*framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Callback: r.handleRawRead,
Summary: "Read the value of the key at the given path.",
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"value": {
Type: framework.TypeString,
Required: true,
},
},
}},
},
Summary: "Read the value of the key at the given path.",
},
logical.UpdateOperation: &framework.PathOperation{
Callback: r.handleRawWrite,
Summary: "Update the value of the key at the given path.",
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
}},
},
Summary: "Update the value of the key at the given path.",
},
logical.CreateOperation: &framework.PathOperation{
Callback: r.handleRawWrite,
Summary: "Create a key with value at the given path.",
Responses: map[int][]framework.Response{
http.StatusNoContent: {{
Description: "OK",
}},
},
Summary: "Create a key with value at the given path.",
},
logical.DeleteOperation: &framework.PathOperation{
Callback: r.handleRawDelete,
Summary: "Delete the key with given path.",
Responses: map[int][]framework.Response{
http.StatusNoContent: {{
Description: "OK",
}},
},
Summary: "Delete the key with given path.",
},
logical.ListOperation: &framework.PathOperation{
Callback: r.handleRawList,
Summary: "Return a list keys for a given path prefix.",
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"keys": {
Type: framework.TypeStringSlice,
Required: true,
},
},
}},
},
Summary: "Return a list keys for a given path prefix.",
},
},

View File

@ -375,6 +375,47 @@ func (b *SystemBackend) configPaths() []*framework.Path {
}
func (b *SystemBackend) rekeyPaths() []*framework.Path {
respFields := map[string]*framework.FieldSchema{
"nounce": {
Type: framework.TypeString,
Required: true,
},
"started": {
Type: framework.TypeString,
Required: true,
},
"t": {
Type: framework.TypeInt,
Required: true,
},
"n": {
Type: framework.TypeInt,
Required: true,
},
"progress": {
Type: framework.TypeInt,
Required: true,
},
"required": {
Type: framework.TypeInt,
Required: true,
},
"verification_required": {
Type: framework.TypeBool,
Required: true,
},
"verification_nonce": {
Type: framework.TypeString,
Required: true,
},
"backup": {
Type: framework.TypeBool,
},
"pgp_fingerprints": {
Type: framework.TypeCommaStringSlice,
},
}
return []*framework.Path{
{
Pattern: "rekey/init",
@ -404,13 +445,30 @@ func (b *SystemBackend) rekeyPaths() []*framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: respFields,
}},
},
Summary: "Reads the configuration and progress of the current rekey attempt.",
},
logical.UpdateOperation: &framework.PathOperation{
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: respFields,
}},
},
Summary: "Initializes a new rekey attempt.",
Description: "Only a single rekey attempt can take place at a time, and changing the parameters of a rekey requires canceling and starting a new rekey, which will also provide a new nonce.",
},
logical.DeleteOperation: &framework.PathOperation{
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
}},
},
Summary: "Cancels any in-progress rekey.",
Description: "This clears the rekey settings as well as any progress made. This must be called to change the parameters of the rekey. Note: verification is still a part of a rekey. If rekeying is canceled during the verification flow, the current unseal keys remain valid.",
},
@ -424,11 +482,35 @@ func (b *SystemBackend) rekeyPaths() []*framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Callback: b.handleRekeyRetrieveBarrier,
Summary: "Return the backup copy of PGP-encrypted unseal keys.",
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"nonce": {
Type: framework.TypeString,
Required: true,
},
"keys": {
Type: framework.TypeMap,
Required: true,
},
"keys_base64": {
Type: framework.TypeMap,
Required: true,
},
},
}},
},
Summary: "Return the backup copy of PGP-encrypted unseal keys.",
},
logical.DeleteOperation: &framework.PathOperation{
Callback: b.handleRekeyDeleteBarrier,
Summary: "Delete the backup copy of PGP-encrypted unseal keys.",
Responses: map[int][]framework.Response{
http.StatusNoContent: {{
Description: "OK",
}},
},
Summary: "Delete the backup copy of PGP-encrypted unseal keys.",
},
},
@ -441,9 +523,37 @@ func (b *SystemBackend) rekeyPaths() []*framework.Path {
Fields: map[string]*framework.FieldSchema{},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.ReadOperation: b.handleRekeyRetrieveRecovery,
logical.DeleteOperation: b.handleRekeyDeleteRecovery,
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Callback: b.handleRekeyRetrieveRecovery,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"nonce": {
Type: framework.TypeString,
Required: true,
},
"keys": {
Type: framework.TypeMap,
Required: true,
},
"keys_base64": {
Type: framework.TypeMap,
Required: true,
},
},
}},
},
},
logical.DeleteOperation: &framework.PathOperation{
Callback: b.handleRekeyDeleteRecovery,
Responses: map[int][]framework.Response{
http.StatusNoContent: {{
Description: "OK",
}},
},
},
},
HelpSynopsis: strings.TrimSpace(sysHelp["rekey_backup"][0]),
@ -465,6 +575,55 @@ func (b *SystemBackend) rekeyPaths() []*framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"nounce": {
Type: framework.TypeString,
Required: true,
},
"complete": {
Type: framework.TypeBool,
},
"started": {
Type: framework.TypeString,
},
"t": {
Type: framework.TypeInt,
},
"n": {
Type: framework.TypeInt,
},
"progress": {
Type: framework.TypeInt,
},
"required": {
Type: framework.TypeInt,
},
"keys": {
Type: framework.TypeCommaStringSlice,
},
"keys_base64": {
Type: framework.TypeCommaStringSlice,
},
"verification_required": {
Type: framework.TypeBool,
Required: true,
},
"verification_nonce": {
Type: framework.TypeString,
Required: true,
},
"backup": {
Type: framework.TypeBool,
},
"pgp_fingerprints": {
Type: framework.TypeCommaStringSlice,
},
},
}},
},
Summary: "Enter a single unseal key share to progress the rekey of the Vault.",
},
},
@ -485,13 +644,81 @@ func (b *SystemBackend) rekeyPaths() []*framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"nounce": {
Type: framework.TypeString,
Required: true,
},
"started": {
Type: framework.TypeString,
Required: true,
},
"t": {
Type: framework.TypeInt,
Required: true,
},
"n": {
Type: framework.TypeInt,
Required: true,
},
"progress": {
Type: framework.TypeInt,
Required: true,
},
},
}},
},
Summary: "Read the configuration and progress of the current rekey verification attempt.",
},
logical.DeleteOperation: &framework.PathOperation{
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"nounce": {
Type: framework.TypeString,
Required: true,
},
"started": {
Type: framework.TypeString,
Required: true,
},
"t": {
Type: framework.TypeInt,
Required: true,
},
"n": {
Type: framework.TypeInt,
Required: true,
},
"progress": {
Type: framework.TypeInt,
Required: true,
},
},
}},
},
Summary: "Cancel any in-progress rekey verification operation.",
Description: "This clears any progress made and resets the nonce. Unlike a `DELETE` against `sys/rekey/init`, this only resets the current verification operation, not the entire rekey atttempt.",
},
logical.UpdateOperation: &framework.PathOperation{
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"nounce": {
Type: framework.TypeString,
Required: true,
},
"complete": {
Type: framework.TypeBool,
},
},
}},
},
Summary: "Enter a single new key share to progress the rekey verification operation.",
},
},
@ -1706,7 +1933,18 @@ func (b *SystemBackend) remountPaths() []*framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.handleRemount,
Summary: "Initiate a mount migration",
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"migration_id": {
Type: framework.TypeString,
Required: true,
},
},
}},
},
Summary: "Initiate a mount migration",
},
},
HelpSynopsis: strings.TrimSpace(sysHelp["remount"][0]),
@ -1725,7 +1963,22 @@ func (b *SystemBackend) remountPaths() []*framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Callback: b.handleRemountStatusCheck,
Summary: "Check status of a mount migration",
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"migration_id": {
Type: framework.TypeString,
Required: true,
},
"migration_info": {
Type: framework.TypeMap,
Required: true,
},
},
}},
},
Summary: "Check status of a mount migration",
},
},
HelpSynopsis: strings.TrimSpace(sysHelp["remount-status"][0]),

View File

@ -2,6 +2,7 @@ package vault
import (
"context"
"net/http"
"strings"
"time"
@ -34,9 +35,33 @@ func (b *SystemBackend) quotasPaths() []*framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.handleQuotasConfigUpdate(),
Responses: map[int][]framework.Response{
http.StatusNoContent: {{
Description: "OK",
}},
},
},
logical.ReadOperation: &framework.PathOperation{
Callback: b.handleQuotasConfigRead(),
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"enable_rate_limit_audit_logging": {
Type: framework.TypeBool,
Required: true,
},
"enable_rate_limit_response_headers": {
Type: framework.TypeBool,
Required: true,
},
"rate_limit_exempt_paths": {
Type: framework.TypeStringSlice,
Required: true,
},
},
}},
},
},
},
HelpSynopsis: strings.TrimSpace(quotasHelp["quotas-config"][0]),
@ -47,6 +72,17 @@ func (b *SystemBackend) quotasPaths() []*framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.ListOperation: &framework.PathOperation{
Callback: b.handleRateLimitQuotasList(),
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"keys": {
Type: framework.TypeStringSlice,
Required: true,
},
},
}},
},
},
},
HelpSynopsis: strings.TrimSpace(quotasHelp["rate-limit-list"][0]),
@ -92,12 +128,57 @@ from any further requests until after the 'block_interval' has elapsed.`,
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.handleRateLimitQuotasUpdate(),
Responses: map[int][]framework.Response{
http.StatusNoContent: {{
Description: http.StatusText(http.StatusNoContent),
}},
},
},
logical.ReadOperation: &framework.PathOperation{
Callback: b.handleRateLimitQuotasRead(),
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"type": {
Type: framework.TypeString,
Required: true,
},
"name": {
Type: framework.TypeString,
Required: true,
},
"path": {
Type: framework.TypeString,
Required: true,
},
"role": {
Type: framework.TypeString,
Required: true,
},
"rate": {
Type: framework.TypeFloat,
Required: true,
},
"interval": {
Type: framework.TypeInt,
Required: true,
},
"block_interval": {
Type: framework.TypeInt,
Required: true,
},
},
}},
},
},
logical.DeleteOperation: &framework.PathOperation{
Callback: b.handleRateLimitQuotasDelete(),
Responses: map[int][]framework.Response{
http.StatusNoContent: {{
Description: "OK",
}},
},
},
},
HelpSynopsis: strings.TrimSpace(quotasHelp["rate-limit"][0]),

View File

@ -758,12 +758,29 @@ func TestSystemBackend_remount_auth(t *testing.T) {
req.Data["config"] = structs.Map(MountConfig{})
resp, err := b.HandleRequest(namespace.RootContext(nil), req)
// validate the response structure for remount named read
schema.ValidateResponse(
t,
schema.GetResponseSchema(t, b.(*SystemBackend).Route(req.Path), req.Operation),
resp,
true,
)
corehelpers.RetryUntil(t, 5*time.Second, func() error {
req = logical.TestRequest(t, logical.ReadOperation, fmt.Sprintf("remount/status/%s", resp.Data["migration_id"]))
resp, err = b.HandleRequest(namespace.RootContext(nil), req)
if err != nil {
t.Fatalf("err: %v", err)
}
// validate the response structure for remount status read
schema.ValidateResponse(
t,
schema.GetResponseSchema(t, b.(*SystemBackend).Route(req.Path), req.Operation),
resp,
true,
)
migrationInfo := resp.Data["migration_info"].(*MountMigrationInfo)
if migrationInfo.MigrationStatus != MigrationSuccessStatus.String() {
return fmt.Errorf("Expected migration status to be successful, got %q", migrationInfo.MigrationStatus)