mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 18:17:55 +00:00
* OpenAPI: Separate ListOperation from ReadOperation Historically, since Vault's ReadOperation and ListOperation both map to the HTTP GET method, their representation in the generated OpenAPI has been a bit confusing. This was partially mitigated some time ago, by making the `list` query parameter express whether it was required or optional - but only in a way useful to human readers - the human had to know, for example, that the schema of the response body would change depending on whether `list` was selected. Now that there is an effort underway to automatically generate API clients from the OpenAPI spec, we have a need to fix this more comprehensively. Fortunately, we do have a means to do so - since Vault has opinionated treatment of trailing slashes, linked to operations being list or not, we can use an added trailing slash on the URL path to separate list operations in the OpenAPI spec. This PR implements that, and then fixes an operation ID which becomes duplicated, with this change applied. See also hashicorp/vault-client-go#174, a bug which will be fixed by this work. * Set further DisplayAttrs in auth/github plugin To mask out more duplicate read/list functionality, now being separately generated to OpenAPI client libraries as a result of this change. * Apply requested changes to operation IDs I'm not totally convinced its worth the extra lines of code, but equally, I don't have strong feelings about it, so I'll just make the change. * Adjust logic to prevent any possibility of generating OpenAPI paths with doubled final slashes Even in the edge case of improper use of regex patterns and operations. * changelog * Fix TestSudoPaths to pass again... which snowballed a bit... Once I looked hard at it, I found it was missing several sudo paths, which led to additional bug fixing elsewhere. I might need to pull some parts of this change out into a separate PR for ease of review... * Fix other tests * More test fixing * Undo scope creep - back away from fixing sudo paths not shown as such in OpenAPI, at least within this PR Just add TODO comments for now.
127 lines
3.5 KiB
Go
127 lines
3.5 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
log "github.com/hashicorp/go-hclog"
|
|
"github.com/hashicorp/vault/api"
|
|
"github.com/hashicorp/vault/helper/builtinplugins"
|
|
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
|
"github.com/hashicorp/vault/vault"
|
|
)
|
|
|
|
const sudoKey = "x-vault-sudo"
|
|
|
|
// Tests that the static list of sudo paths in the api package matches what's in the current OpenAPI spec.
|
|
func TestSudoPaths(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
coreConfig := &vault.CoreConfig{
|
|
DisableMlock: true,
|
|
DisableCache: true,
|
|
EnableRaw: true,
|
|
EnableIntrospection: true,
|
|
Logger: log.NewNullLogger(),
|
|
BuiltinRegistry: builtinplugins.Registry,
|
|
}
|
|
client, _, closer := testVaultServerCoreConfig(t, coreConfig)
|
|
defer closer()
|
|
|
|
// At present there are no auth methods with sudo paths, except for the automatically mounted token backend
|
|
for _, credBackendName := range []string{} {
|
|
err := client.Sys().EnableAuthWithOptions(credBackendName, &api.EnableAuthOptions{
|
|
Type: credBackendName,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("error enabling auth backend for test: %v", err)
|
|
}
|
|
}
|
|
|
|
// Each secrets engine that contains sudo paths (other than automatically mounted ones) must be mounted here
|
|
for _, logicalBackendName := range []string{"pki"} {
|
|
err := client.Sys().Mount(logicalBackendName, &api.MountInput{
|
|
Type: logicalBackendName,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("error enabling logical backend for test: %v", err)
|
|
}
|
|
}
|
|
|
|
sudoPathsFromSpec, err := getSudoPathsFromSpec(client)
|
|
if err != nil {
|
|
t.Fatalf("error getting list of paths that require sudo from OpenAPI endpoint: %v", err)
|
|
}
|
|
|
|
sudoPathsInCode := api.SudoPaths()
|
|
|
|
// check for missing paths
|
|
for path := range sudoPathsFromSpec {
|
|
pathTrimmed := strings.TrimRight(path, "/")
|
|
if _, ok := sudoPathsInCode[pathTrimmed]; !ok {
|
|
t.Fatalf(
|
|
"A path in the OpenAPI spec is missing from the static list of "+
|
|
"sudo paths in the api module (%s). Please reconcile the two "+
|
|
"accordingly.", pathTrimmed)
|
|
}
|
|
}
|
|
|
|
// check for extra paths
|
|
for path := range sudoPathsInCode {
|
|
if _, ok := sudoPathsFromSpec[path]; !ok {
|
|
if _, ok := sudoPathsFromSpec[path+"/"]; !ok {
|
|
t.Fatalf(
|
|
"A path in the static list of sudo paths in the api module "+
|
|
"is not marked as a sudo path in the OpenAPI spec (%s). Please reconcile the two "+
|
|
"accordingly.", path)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func getSudoPathsFromSpec(client *api.Client) (map[string]struct{}, error) {
|
|
r := client.NewRequest("GET", "/v1/sys/internal/specs/openapi")
|
|
resp, err := client.RawRequest(r)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to retrieve sudo endpoints: %v", err)
|
|
}
|
|
if resp != nil {
|
|
defer resp.Body.Close()
|
|
}
|
|
|
|
oasInfo := make(map[string]interface{})
|
|
if err := jsonutil.DecodeJSONFromReader(resp.Body, &oasInfo); err != nil {
|
|
return nil, fmt.Errorf("unable to decode JSON from OpenAPI response: %v", err)
|
|
}
|
|
|
|
paths, ok := oasInfo["paths"]
|
|
if !ok {
|
|
return nil, fmt.Errorf("OpenAPI response did not include paths")
|
|
}
|
|
|
|
pathsMap, ok := paths.(map[string]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("OpenAPI response did not return valid paths")
|
|
}
|
|
|
|
sudoPaths := make(map[string]struct{})
|
|
for pathName, pathInfo := range pathsMap {
|
|
pathInfoMap, ok := pathInfo.(map[string]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
if sudo, ok := pathInfoMap[sudoKey]; ok {
|
|
if sudo == true {
|
|
sudoPaths[pathName] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
|
|
return sudoPaths, nil
|
|
}
|