mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 03:27:54 +00:00
Add mount path into the default generated openapi.json spec (UI) (#17926)
This commit is contained in:
committed by
GitHub
parent
256fca684b
commit
db8efac708
@@ -37,9 +37,9 @@ const (
|
|||||||
// path matches that path or not (useful specifically for the paths that
|
// path matches that path or not (useful specifically for the paths that
|
||||||
// contain templated fields.)
|
// contain templated fields.)
|
||||||
var sudoPaths = map[string]*regexp.Regexp{
|
var sudoPaths = map[string]*regexp.Regexp{
|
||||||
"/auth/token/accessors/": regexp.MustCompile(`^/auth/token/accessors/$`),
|
"/auth/{token_mount_path}/accessors/": regexp.MustCompile(`^/auth/.+/accessors/$`),
|
||||||
"/pki/root": regexp.MustCompile(`^/pki/root$`),
|
"/{pki_mount_path}/root": regexp.MustCompile(`^/.+/root$`),
|
||||||
"/pki/root/sign-self-issued": regexp.MustCompile(`^/pki/root/sign-self-issued$`),
|
"/{pki_mount_path}/root/sign-self-issued": regexp.MustCompile(`^/.+/root/sign-self-issued$`),
|
||||||
"/sys/audit": regexp.MustCompile(`^/sys/audit$`),
|
"/sys/audit": regexp.MustCompile(`^/sys/audit$`),
|
||||||
"/sys/audit/{path}": regexp.MustCompile(`^/sys/audit/.+$`),
|
"/sys/audit/{path}": regexp.MustCompile(`^/sys/audit/.+$`),
|
||||||
"/sys/auth/{path}": regexp.MustCompile(`^/sys/auth/.+$`),
|
"/sys/auth/{path}": regexp.MustCompile(`^/sys/auth/.+$`),
|
||||||
|
|||||||
@@ -539,13 +539,6 @@ func (b *Backend) handleRootHelp(req *logical.Request) (*logical.Response, error
|
|||||||
// names in the OAS document.
|
// names in the OAS document.
|
||||||
requestResponsePrefix := req.GetString("requestResponsePrefix")
|
requestResponsePrefix := req.GetString("requestResponsePrefix")
|
||||||
|
|
||||||
// Generic mount paths will primarily be used for code generation purposes.
|
|
||||||
// This will result in dynamic mount paths being placed instead of
|
|
||||||
// hardcoded default paths. For example /auth/approle/login would be replaced
|
|
||||||
// with /auth/{mountPath}/login. This will be replaced for all secrets
|
|
||||||
// engines and auth methods that are enabled.
|
|
||||||
genericMountPaths, _ := req.Get("genericMountPaths").(bool)
|
|
||||||
|
|
||||||
// Build OpenAPI response for the entire backend
|
// Build OpenAPI response for the entire backend
|
||||||
vaultVersion := "unknown"
|
vaultVersion := "unknown"
|
||||||
if b.System() != nil {
|
if b.System() != nil {
|
||||||
@@ -557,7 +550,7 @@ func (b *Backend) handleRootHelp(req *logical.Request) (*logical.Response, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
doc := NewOASDocument(vaultVersion)
|
doc := NewOASDocument(vaultVersion)
|
||||||
if err := documentPaths(b, requestResponsePrefix, genericMountPaths, doc); err != nil {
|
if err := documentPaths(b, requestResponsePrefix, doc); err != nil {
|
||||||
b.Logger().Warn("error generating OpenAPI", "error", err)
|
b.Logger().Warn("error generating OpenAPI", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -208,16 +208,16 @@ var (
|
|||||||
altRootsRe = regexp.MustCompile(`^\(([\w\-_]+(?:\|[\w\-_]+)+)\)(/.*)$`) // Pattern starting with alts, e.g. "(root1|root2)/(?P<name>regex)"
|
altRootsRe = regexp.MustCompile(`^\(([\w\-_]+(?:\|[\w\-_]+)+)\)(/.*)$`) // Pattern starting with alts, e.g. "(root1|root2)/(?P<name>regex)"
|
||||||
cleanCharsRe = regexp.MustCompile("[()^$?]") // Set of regex characters that will be stripped during cleaning
|
cleanCharsRe = regexp.MustCompile("[()^$?]") // Set of regex characters that will be stripped during cleaning
|
||||||
cleanSuffixRe = regexp.MustCompile(`/\?\$?$`) // Path suffix patterns that will be stripped during cleaning
|
cleanSuffixRe = regexp.MustCompile(`/\?\$?$`) // Path suffix patterns that will be stripped during cleaning
|
||||||
nonWordRe = regexp.MustCompile(`[^\w]+`) // Match a sequence of non-word characters
|
nonWordRe = regexp.MustCompile(`[^a-zA-Z0-9]+`) // Match a sequence of non-word characters
|
||||||
pathFieldsRe = regexp.MustCompile(`{(\w+)}`) // Capture OpenAPI-style named parameters, e.g. "lookup/{urltoken}",
|
pathFieldsRe = regexp.MustCompile(`{(\w+)}`) // Capture OpenAPI-style named parameters, e.g. "lookup/{urltoken}",
|
||||||
reqdRe = regexp.MustCompile(`\(?\?P<(\w+)>[^)]*\)?`) // Capture required parameters, e.g. "(?P<name>regex)"
|
reqdRe = regexp.MustCompile(`\(?\?P<(\w+)>[^)]*\)?`) // Capture required parameters, e.g. "(?P<name>regex)"
|
||||||
wsRe = regexp.MustCompile(`\s+`) // Match whitespace, to be compressed during cleaning
|
wsRe = regexp.MustCompile(`\s+`) // Match whitespace, to be compressed during cleaning
|
||||||
)
|
)
|
||||||
|
|
||||||
// documentPaths parses all paths in a framework.Backend into OpenAPI paths.
|
// documentPaths parses all paths in a framework.Backend into OpenAPI paths.
|
||||||
func documentPaths(backend *Backend, requestResponsePrefix string, genericMountPaths bool, doc *OASDocument) error {
|
func documentPaths(backend *Backend, requestResponsePrefix string, doc *OASDocument) error {
|
||||||
for _, p := range backend.Paths {
|
for _, p := range backend.Paths {
|
||||||
if err := documentPath(p, backend.SpecialPaths(), requestResponsePrefix, genericMountPaths, backend.BackendType, doc); err != nil {
|
if err := documentPath(p, backend.SpecialPaths(), requestResponsePrefix, backend.BackendType, doc); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,7 +226,7 @@ func documentPaths(backend *Backend, requestResponsePrefix string, genericMountP
|
|||||||
}
|
}
|
||||||
|
|
||||||
// documentPath parses a framework.Path into one or more OpenAPI paths.
|
// documentPath parses a framework.Path into one or more OpenAPI paths.
|
||||||
func documentPath(p *Path, specialPaths *logical.Paths, requestResponsePrefix string, genericMountPaths bool, backendType logical.BackendType, doc *OASDocument) error {
|
func documentPath(p *Path, specialPaths *logical.Paths, requestResponsePrefix string, backendType logical.BackendType, doc *OASDocument) error {
|
||||||
var sudoPaths []string
|
var sudoPaths []string
|
||||||
var unauthPaths []string
|
var unauthPaths []string
|
||||||
|
|
||||||
@@ -265,16 +265,21 @@ func documentPath(p *Path, specialPaths *logical.Paths, requestResponsePrefix st
|
|||||||
// Body fields will be added to individual operations.
|
// Body fields will be added to individual operations.
|
||||||
pathFields, bodyFields := splitFields(p.Fields, path)
|
pathFields, bodyFields := splitFields(p.Fields, path)
|
||||||
|
|
||||||
if genericMountPaths && requestResponsePrefix != "system" && requestResponsePrefix != "identity" {
|
defaultMountPath := requestResponsePrefix
|
||||||
// Add mount path as a parameter
|
if requestResponsePrefix == "kv" {
|
||||||
|
defaultMountPath = "secret"
|
||||||
|
}
|
||||||
|
|
||||||
|
if defaultMountPath != "system" && defaultMountPath != "identity" {
|
||||||
p := OASParameter{
|
p := OASParameter{
|
||||||
Name: "mountPath",
|
Name: fmt.Sprintf("%s_mount_path", defaultMountPath),
|
||||||
Description: "Path that the backend was mounted at",
|
Description: "Path where the backend was mounted; the endpoint path will be offset by the mount path",
|
||||||
In: "path",
|
In: "path",
|
||||||
Schema: &OASSchema{
|
Schema: &OASSchema{
|
||||||
Type: "string",
|
Type: "string",
|
||||||
|
Default: defaultMountPath,
|
||||||
},
|
},
|
||||||
Required: true,
|
Required: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
pi.Parameters = append(pi.Parameters, p)
|
pi.Parameters = append(pi.Parameters, p)
|
||||||
@@ -780,6 +785,9 @@ func cleanResponse(resp *logical.Response) *cleanedResponse {
|
|||||||
//
|
//
|
||||||
// An optional user-provided suffix ("context") may also be appended.
|
// An optional user-provided suffix ("context") may also be appended.
|
||||||
func (d *OASDocument) CreateOperationIDs(context string) {
|
func (d *OASDocument) CreateOperationIDs(context string) {
|
||||||
|
// title caser
|
||||||
|
title := cases.Title(language.English)
|
||||||
|
|
||||||
opIDCount := make(map[string]int)
|
opIDCount := make(map[string]int)
|
||||||
var paths []string
|
var paths []string
|
||||||
|
|
||||||
@@ -806,9 +814,12 @@ func (d *OASDocument) CreateOperationIDs(context string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Discard "_mount_path" from any {thing_mount_path} parameters
|
||||||
|
path = strings.Replace(path, "_mount_path", "", 1)
|
||||||
|
|
||||||
// Space-split on non-words, title case everything, recombine
|
// Space-split on non-words, title case everything, recombine
|
||||||
opID := nonWordRe.ReplaceAllString(strings.ToLower(path), " ")
|
opID := nonWordRe.ReplaceAllString(strings.ToLower(path), " ")
|
||||||
opID = strings.Title(opID)
|
opID = title.String(opID)
|
||||||
opID = method + strings.ReplaceAll(opID, " ", "")
|
opID = method + strings.ReplaceAll(opID, " ", "")
|
||||||
|
|
||||||
// deduplicate operationIds. This is a safeguard, since generated IDs should
|
// deduplicate operationIds. This is a safeguard, since generated IDs should
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ func TestOpenAPI_SpecialPaths(t *testing.T) {
|
|||||||
Root: test.rootPaths,
|
Root: test.rootPaths,
|
||||||
Unauthenticated: test.unauthPaths,
|
Unauthenticated: test.unauthPaths,
|
||||||
}
|
}
|
||||||
err := documentPath(&path, sp, "kv", false, logical.TypeLogical, doc)
|
err := documentPath(&path, sp, "kv", logical.TypeLogical, doc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -528,11 +528,11 @@ func TestOpenAPI_OperationID(t *testing.T) {
|
|||||||
|
|
||||||
for _, context := range []string{"", "bar"} {
|
for _, context := range []string{"", "bar"} {
|
||||||
doc := NewOASDocument("version")
|
doc := NewOASDocument("version")
|
||||||
err := documentPath(path1, nil, "kv", false, logical.TypeLogical, doc)
|
err := documentPath(path1, nil, "kv", logical.TypeLogical, doc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err = documentPath(path2, nil, "kv", false, logical.TypeLogical, doc)
|
err = documentPath(path2, nil, "kv", logical.TypeLogical, doc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -592,7 +592,7 @@ func TestOpenAPI_CustomDecoder(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
docOrig := NewOASDocument("version")
|
docOrig := NewOASDocument("version")
|
||||||
err := documentPath(p, nil, "kv", false, logical.TypeLogical, docOrig)
|
err := documentPath(p, nil, "kv", logical.TypeLogical, docOrig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -655,7 +655,7 @@ func testPath(t *testing.T, path *Path, sp *logical.Paths, expectedJSON string)
|
|||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
doc := NewOASDocument("dummyversion")
|
doc := NewOASDocument("dummyversion")
|
||||||
if err := documentPath(path, sp, "kv", false, logical.TypeLogical, doc); err != nil {
|
if err := documentPath(path, sp, "kv", logical.TypeLogical, doc); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
doc.CreateOperationIDs("")
|
doc.CreateOperationIDs("")
|
||||||
@@ -665,6 +665,8 @@ func testPath(t *testing.T, path *Path, sp *logical.Paths, expectedJSON string)
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.Log(string(docJSON))
|
||||||
|
|
||||||
// Compare json by first decoding, then comparing with a deep equality check.
|
// Compare json by first decoding, then comparing with a deep equality check.
|
||||||
var expected, actual interface{}
|
var expected, actual interface{}
|
||||||
if err := jsonutil.DecodeJSON(docJSON, &actual); err != nil {
|
if err := jsonutil.DecodeJSON(docJSON, &actual); err != nil {
|
||||||
|
|||||||
@@ -330,7 +330,7 @@ func (p *Path) helpCallback(b *Backend) OperationFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
doc := NewOASDocument(vaultVersion)
|
doc := NewOASDocument(vaultVersion)
|
||||||
if err := documentPath(p, b.SpecialPaths(), requestResponsePrefix, false, b.BackendType, doc); err != nil {
|
if err := documentPath(p, b.SpecialPaths(), requestResponsePrefix, b.BackendType, doc); err != nil {
|
||||||
b.Logger().Warn("error generating OpenAPI", "error", err)
|
b.Logger().Warn("error generating OpenAPI", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
sdk/framework/testdata/legacy.json
vendored
9
sdk/framework/testdata/legacy.json
vendored
@@ -21,6 +21,15 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "secret_mount_path",
|
||||||
|
"description": "Path where the backend was mounted; the endpoint path will be offset by the mount path",
|
||||||
|
"in": "path",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "secret"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"get": {
|
"get": {
|
||||||
|
|||||||
9
sdk/framework/testdata/operations.json
vendored
9
sdk/framework/testdata/operations.json
vendored
@@ -34,6 +34,15 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "secret_mount_path",
|
||||||
|
"description": "Path where the backend was mounted; the endpoint path will be offset by the mount path",
|
||||||
|
"in": "path",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "secret"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"get": {
|
"get": {
|
||||||
|
|||||||
9
sdk/framework/testdata/operations_list.json
vendored
9
sdk/framework/testdata/operations_list.json
vendored
@@ -33,6 +33,15 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "secret_mount_path",
|
||||||
|
"description": "Path where the backend was mounted; the endpoint path will be offset by the mount path",
|
||||||
|
"in": "path",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "secret"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"get": {
|
"get": {
|
||||||
|
|||||||
11
sdk/framework/testdata/responses.json
vendored
11
sdk/framework/testdata/responses.json
vendored
@@ -12,6 +12,17 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"/foo": {
|
"/foo": {
|
||||||
"description": "Synopsis",
|
"description": "Synopsis",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "secret_mount_path",
|
||||||
|
"description": "Path where the backend was mounted; the endpoint path will be offset by the mount path",
|
||||||
|
"in": "path",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "secret"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"x-vault-unauthenticated": true,
|
"x-vault-unauthenticated": true,
|
||||||
"delete": {
|
"delete": {
|
||||||
"operationId": "deleteFoo",
|
"operationId": "deleteFoo",
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import Model from '@ember-data/model';
|
|||||||
import Service from '@ember/service';
|
import Service from '@ember/service';
|
||||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
import { getOwner } from '@ember/application';
|
import { getOwner } from '@ember/application';
|
||||||
import { assign } from '@ember/polyfills';
|
|
||||||
import { expandOpenApiProps, combineAttributes } from 'vault/utils/openapi-to-attrs';
|
import { expandOpenApiProps, combineAttributes } from 'vault/utils/openapi-to-attrs';
|
||||||
import fieldToAttrs from 'vault/utils/field-to-attrs';
|
import fieldToAttrs from 'vault/utils/field-to-attrs';
|
||||||
import { resolve, reject } from 'rsvp';
|
import { resolve, reject } from 'rsvp';
|
||||||
@@ -179,31 +178,36 @@ export default Service.extend({
|
|||||||
// Returns relevant information from OpenAPI
|
// Returns relevant information from OpenAPI
|
||||||
// as determined by the expandOpenApiProps util
|
// as determined by the expandOpenApiProps util
|
||||||
getProps(helpUrl, backend) {
|
getProps(helpUrl, backend) {
|
||||||
// add name of thing you want
|
|
||||||
debug(`Fetching schema properties for ${backend} from ${helpUrl}`);
|
debug(`Fetching schema properties for ${backend} from ${helpUrl}`);
|
||||||
|
|
||||||
return this.ajax(helpUrl, backend).then((help) => {
|
return this.ajax(helpUrl, backend).then((help) => {
|
||||||
// paths is an array but it will have a single entry
|
// help.openapi.paths is an array with one item
|
||||||
// for the scope we're in
|
const path = Object.keys(help.openapi.paths)[0];
|
||||||
const path = Object.keys(help.openapi.paths)[0]; // do this or look at name
|
|
||||||
const pathInfo = help.openapi.paths[path];
|
const pathInfo = help.openapi.paths[path];
|
||||||
const params = pathInfo.parameters;
|
const params = pathInfo.parameters;
|
||||||
const paramProp = {};
|
const paramProp = {};
|
||||||
|
|
||||||
// include url params
|
// include url params
|
||||||
if (params) {
|
if (params) {
|
||||||
const { name, schema, description } = params[0];
|
params.forEach((param) => {
|
||||||
const label = capitalize(name.split('_').join(' '));
|
const { name, schema, description } = param;
|
||||||
|
if (name === '_mount_path') {
|
||||||
|
// this param refers to the engine mount path,
|
||||||
|
// which is already accounted for as backend
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const label = capitalize(name.split('_').join(' '));
|
||||||
|
|
||||||
paramProp[name] = {
|
paramProp[name] = {
|
||||||
'x-vault-displayAttrs': {
|
'x-vault-displayAttrs': {
|
||||||
name: label,
|
name: label,
|
||||||
group: 'default',
|
group: 'default',
|
||||||
},
|
},
|
||||||
type: schema.type,
|
type: schema.type,
|
||||||
description: description,
|
description: description,
|
||||||
isId: true,
|
isId: true,
|
||||||
};
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let props = {};
|
let props = {};
|
||||||
@@ -220,7 +224,7 @@ export default Service.extend({
|
|||||||
}
|
}
|
||||||
// put url params (e.g. {name}, {role})
|
// put url params (e.g. {name}, {role})
|
||||||
// at the front of the props list
|
// at the front of the props list
|
||||||
const newProps = assign({}, paramProp, props);
|
const newProps = { ...paramProp, ...props };
|
||||||
return expandOpenApiProps(newProps);
|
return expandOpenApiProps(newProps);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4440,8 +4440,6 @@ func (b *SystemBackend) pathInternalOpenAPI(ctx context.Context, req *logical.Re
|
|||||||
// be received from plugin backends.
|
// be received from plugin backends.
|
||||||
doc := framework.NewOASDocument(version.Version)
|
doc := framework.NewOASDocument(version.Version)
|
||||||
|
|
||||||
genericMountPaths, _ := d.Get("generic_mount_paths").(bool)
|
|
||||||
|
|
||||||
procMountGroup := func(group, mountPrefix string) error {
|
procMountGroup := func(group, mountPrefix string) error {
|
||||||
for mount, entry := range resp.Data[group].(map[string]interface{}) {
|
for mount, entry := range resp.Data[group].(map[string]interface{}) {
|
||||||
|
|
||||||
@@ -4459,7 +4457,7 @@ func (b *SystemBackend) pathInternalOpenAPI(ctx context.Context, req *logical.Re
|
|||||||
req := &logical.Request{
|
req := &logical.Request{
|
||||||
Operation: logical.HelpOperation,
|
Operation: logical.HelpOperation,
|
||||||
Storage: req.Storage,
|
Storage: req.Storage,
|
||||||
Data: map[string]interface{}{"requestResponsePrefix": pluginType, "genericMountPaths": genericMountPaths},
|
Data: map[string]interface{}{"requestResponsePrefix": pluginType},
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := backend.HandleRequest(ctx, req)
|
resp, err := backend.HandleRequest(ctx, req)
|
||||||
@@ -4513,12 +4511,16 @@ func (b *SystemBackend) pathInternalOpenAPI(ctx context.Context, req *logical.Re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if genericMountPaths && mount != "sys/" && mount != "identity/" {
|
var docPath string
|
||||||
s := fmt.Sprintf("/%s{mountPath}/%s", mountPrefix, path)
|
if mount == "kv/" {
|
||||||
doc.Paths[s] = obj
|
docPath = fmt.Sprintf("/%s{secret_mount_path}/%s", mountPrefix, path)
|
||||||
|
} else if mount != "sys/" && mount != "identity/" {
|
||||||
|
docPath = fmt.Sprintf("/%s{%s_mount_path}/%s", mountPrefix, strings.TrimRight(mount, "/"), path)
|
||||||
} else {
|
} else {
|
||||||
doc.Paths["/"+mountPrefix+mount+path] = obj
|
docPath = fmt.Sprintf("/%s%s%s", mountPrefix, mount, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doc.Paths[docPath] = obj
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge backend schema components
|
// Merge backend schema components
|
||||||
@@ -5028,9 +5030,7 @@ func sanitizePath(path string) string {
|
|||||||
path += "/"
|
path += "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(path, "/") {
|
path = strings.TrimPrefix(path, "/")
|
||||||
path = path[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3602,66 +3602,13 @@ func TestSystemBackend_InternalUIMount(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSystemBackend_OASGenericMount(t *testing.T) {
|
|
||||||
_, b, rootToken := testCoreSystemBackend(t)
|
|
||||||
var oapi map[string]interface{}
|
|
||||||
|
|
||||||
// Check that default paths are present with a root token
|
|
||||||
req := logical.TestRequest(t, logical.ReadOperation, "internal/specs/openapi")
|
|
||||||
req.Data["generic_mount_paths"] = true
|
|
||||||
req.ClientToken = rootToken
|
|
||||||
resp, err := b.HandleRequest(namespace.RootContext(nil), req)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
body := resp.Data["http_raw_body"].([]byte)
|
|
||||||
err = jsonutil.DecodeJSON(body, &oapi)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
doc, err := framework.NewOASDocumentFromMap(oapi)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pathSamples := []struct {
|
|
||||||
path string
|
|
||||||
tag string
|
|
||||||
}{
|
|
||||||
{"/auth/{mountPath}/lookup", "auth"},
|
|
||||||
{"/{mountPath}/{path}", "secrets"},
|
|
||||||
{"/identity/group/id", "identity"},
|
|
||||||
{"/{mountPath}/.*", "secrets"},
|
|
||||||
{"/sys/policy", "system"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, path := range pathSamples {
|
|
||||||
if doc.Paths[path.path] == nil {
|
|
||||||
t.Fatalf("didn't find expected path '%s'.", path)
|
|
||||||
}
|
|
||||||
tag := doc.Paths[path.path].Get.Tags[0]
|
|
||||||
if tag != path.tag {
|
|
||||||
t.Fatalf("path: %s; expected tag: %s, actual: %s", path.path, tag, path.tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple check of response size (which is much larger than most
|
|
||||||
// Vault responses), mainly to catch mass omission of expected path data.
|
|
||||||
const minLen = 70000
|
|
||||||
if len(body) < minLen {
|
|
||||||
t.Fatalf("response size too small; expected: min %d, actual: %d", minLen, len(body))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSystemBackend_OpenAPI(t *testing.T) {
|
func TestSystemBackend_OpenAPI(t *testing.T) {
|
||||||
_, b, rootToken := testCoreSystemBackend(t)
|
_, b, rootToken := testCoreSystemBackend(t)
|
||||||
var oapi map[string]interface{}
|
var oapi map[string]interface{}
|
||||||
|
|
||||||
// Ensure no paths are reported if there is no token
|
// Ensure no paths are reported if there is no token
|
||||||
req := logical.TestRequest(t, logical.ReadOperation, "internal/specs/openapi")
|
req := logical.TestRequest(t, logical.ReadOperation, "internal/specs/openapi")
|
||||||
resp, err := b.HandleRequest(namespace.RootContext(nil), req)
|
resp, err := b.HandleRequest(namespace.RootContext(context.Background()), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
@@ -3715,10 +3662,10 @@ func TestSystemBackend_OpenAPI(t *testing.T) {
|
|||||||
path string
|
path string
|
||||||
tag string
|
tag string
|
||||||
}{
|
}{
|
||||||
{"/auth/token/lookup", "auth"},
|
{"/auth/{token_mount_path}/lookup", "auth"},
|
||||||
{"/cubbyhole/{path}", "secrets"},
|
{"/{cubbyhole_mount_path}/{path}", "secrets"},
|
||||||
{"/identity/group/id", "identity"},
|
{"/identity/group/id", "identity"},
|
||||||
{"/secret/.*", "secrets"}, // TODO update after kv repo update
|
{"/{secret_mount_path}/.*", "secrets"}, // TODO update after kv repo update
|
||||||
{"/sys/policy", "system"},
|
{"/sys/policy", "system"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user