open-vault/sdk/framework/path.go

431 lines
16 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package framework
import (
"context"
"fmt"
"sort"
2015-04-03 05:42:05 +00:00
"strings"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/sdk/helper/license"
"github.com/hashicorp/vault/sdk/logical"
)
// Helper which returns a generic regex string for creating endpoint patterns
// that are identified by the given name in the backends
func GenericNameRegex(name string) string {
return fmt.Sprintf("(?P<%s>\\w(([\\w-.]+)?\\w)?)", name)
}
// GenericNameWithAtRegex returns a generic regex that allows alphanumeric
// characters along with -, . and @.
func GenericNameWithAtRegex(name string) string {
return fmt.Sprintf("(?P<%s>\\w(([\\w-.@]+)?\\w)?)", name)
}
// Helper which returns a regex string for optionally accepting the a field
// from the API URL
func OptionalParamRegex(name string) string {
return fmt.Sprintf("(/(?P<%s>.+))?", name)
}
// Helper which returns a regex string for capturing an entire endpoint path
// as the given name.
func MatchAllRegex(name string) string {
return fmt.Sprintf(`(?P<%s>.*)`, name)
}
2015-04-05 01:40:21 +00:00
// PathAppend is a helper for appending lists of paths into a single
// list.
func PathAppend(paths ...[]*Path) []*Path {
result := make([]*Path, 0, 10)
for _, ps := range paths {
result = append(result, ps...)
}
return result
}
// Path is a single path that the backend responds to.
type Path struct {
// Pattern is the pattern of the URL that matches this path.
//
// This should be a valid regular expression. Named captures will be
// exposed as fields that should map to a schema in Fields. If a named
// capture is not a field in the Fields map, then it will be ignored.
//
// The pattern will automatically have a ^ prepended and a $ appended before
// use, if these are not already present, so these may be omitted for clarity.
//
// If a ListOperation is being defined, the pattern must end with /? to match
// a trailing slash optionally, as ListOperations are always processed with a
// trailing slash added to the path if not already present. The match must not
// require the presence of a trailing slash, as HelpOperations, even for a
// path which only implements ListOperation, are processed without a trailing
// slash - so failure to make the trailing slash optional will break the
// `vault path-help` command for the path.
Pattern string
// Fields is the mapping of data fields to a schema describing that
// field.
//
// Field values are obtained from:
//
// - Named captures in the Pattern.
//
// - Parameters in the HTTP request body, for HTTP methods where a
// request body is expected, i.e. PUT/POST/PATCH. The request body is
// typically formatted as JSON, though
// "application/x-www-form-urlencoded" format can also be accepted.
//
// - Parameters in the HTTP URL query-string, for HTTP methods where
// there is no request body, i.e. GET/LIST/DELETE. The query-string
// is *not* parsed at all for PUT/POST/PATCH requests.
//
// Should the same field be specified both as a named capture and as
// a parameter, the named capture takes precedence, and a warning is
// returned.
Fields map[string]*FieldSchema
// Operations is the set of operations supported and the associated OperationsHandler.
//
// If both Create and Update operations are present, documentation and examples from
// the Update definition will be used. Similarly if both Read and List are present,
// Read will be used for documentation.
Operations map[logical.Operation]OperationHandler
// Callbacks are the set of callbacks that are called for a given
// operation. If a callback for a specific operation is not present,
// then logical.ErrUnsupportedOperation is automatically generated.
//
// The help operation is the only operation that the Path will
// automatically handle if the Help field is set. If both the Help
// field is set and there is a callback registered here, then the
// callback will be called.
//
// Deprecated: Operations should be used instead and will take priority if present.
Callbacks map[logical.Operation]OperationFunc
// ExistenceCheck, if implemented, is used to query whether a given
// resource exists or not. This is used for ACL purposes: if an Update
// action is specified, and the existence check returns false, the action
// is not allowed since the resource must first be created. The reverse is
// also true. If not specified, the Update action is forced and the user
// must have UpdateCapability on the path.
ExistenceCheck ExistenceFunc
2018-09-18 03:03:00 +00:00
// FeatureRequired, if implemented, will validate if the given feature is
// enabled for the set of paths
FeatureRequired license.Features
// Deprecated denotes that this path is considered deprecated. This may
// be reflected in help and documentation.
Deprecated bool
// Help is text describing how to use this path. This will be used
// to auto-generate the help operation. The Path will automatically
// generate a parameter listing and URL structure based on the
// regular expression, so the help text should just contain a description
// of what happens.
//
// HelpSynopsis is a one-sentence description of the path. This will
// be automatically line-wrapped at 80 characters.
//
// HelpDescription is a long-form description of the path. This will
// be automatically line-wrapped at 80 characters.
HelpSynopsis string
HelpDescription string
// DisplayAttrs provides hints for UI and documentation generators. They
// will be included in OpenAPI output if set.
DisplayAttrs *DisplayAttributes
// TakesArbitraryInput is used for endpoints that take arbitrary input, instead
// of or as well as their Fields. This is taken into account when printing
// warnings about ignored fields. If this is set, we will not warn when data is
// provided that is not part of the Fields declaration.
TakesArbitraryInput bool
}
// OperationHandler defines and describes a specific operation handler.
type OperationHandler interface {
Handler() OperationFunc
Properties() OperationProperties
}
// OperationProperties describes an operation for documentation, help text,
// and other clients. A Summary should always be provided, whereas other
// fields can be populated as needed.
type OperationProperties struct {
// Summary is a brief (usually one line) description of the operation.
Summary string
// Description is extended documentation of the operation and may contain
// Markdown-formatted text markup.
Description string
// Examples provides samples of the expected request data. The most
// relevant example should be first in the list, as it will be shown in
// documentation that supports only a single example.
Examples []RequestExample
// Responses provides a list of response description for a given response
// code. The most relevant response should be first in the list, as it will
// be shown in documentation that only allows a single example.
Responses map[int][]Response
// Unpublished indicates that this operation should not appear in public
// documentation or help text. The operation may still have documentation
// attached that can be used internally.
Unpublished bool
// Deprecated indicates that this operation should be avoided.
Deprecated bool
// The ForwardPerformance* parameters tell the router to unconditionally forward requests
// to this path if the processing node is a performance secondary/standby. This is generally
// *not* needed as there is already handling in place to automatically forward requests
// that try to write to storage. But there are a few cases where explicit forwarding is needed,
// for example:
//
// * The handler makes requests to other systems (e.g. an external API, database, ...) that
// change external state somehow, and subsequently writes to storage. In this case the
// default forwarding logic could result in multiple mutative calls to the external system.
//
// * The operation spans multiple requests (e.g. an OIDC callback), in-memory caching used,
// and the same node (and therefore cache) should process both steps.
//
// If explicit forwarding is needed, it is usually true that forwarding from both performance
// standbys and performance secondaries should be enabled.
//
// ForwardPerformanceStandby indicates that this path should not be processed
// on a performance standby node, and should be forwarded to the active node instead.
ForwardPerformanceStandby bool
// ForwardPerformanceSecondary indicates that this path should not be processed
// on a performance secondary node, and should be forwarded to the active node instead.
ForwardPerformanceSecondary bool
// DisplayAttrs provides hints for UI and documentation generators. They
// will be included in OpenAPI output if set.
DisplayAttrs *DisplayAttributes
}
type DisplayAttributes struct {
// Name is the name of the field suitable as a label or documentation heading.
Name string `json:"name,omitempty"`
// Description of the field that renders as tooltip help text beside the label (name) in the UI.
// This may be used to replace descriptions that reference comma separation but correspond
// to UI inputs where only arrays are valid. For example params with Type: framework.TypeCommaStringSlice
Description string `json:"description,omitempty"`
// Value is a sample value to display for this field. This may be used
// to indicate a default value, but it is for display only and completely separate
// from any Default member handling.
Value interface{} `json:"value,omitempty"`
// Sensitive indicates that the value should be masked by default in the UI.
Sensitive bool `json:"sensitive,omitempty"`
// Navigation indicates that the path should be available as a navigation tab
Navigation bool `json:"navigation,omitempty"`
Enable generated items for more auth methods (#7513) * enable auth method item configuration in go code * properly parse and list generated items * make sure we only set name on attrs if a label comes from openAPI * correctly construct paths object for method index route * set sensitive property on password for userpass * remove debugger statements * pass method model to list route template to use paths on model for tabs * update tab generation in generated item list, undo enabling userpass users * enable openapi generated itams for certs and userpass, update ldap to no longer have action on list endpoint * add editType to DisplayAttributes, pull tokenutil fields into field group * show sensitive message for sensitive fields displayed in fieldGroupShow component * grab sensitive and editType fields from displayAttrs in openapi-to-attrs util * make sure we don't ask for paths for secret backends since that isn't setup yet * fix styling of sensitive text for fieldGroupShow component * update openapi-to-attrs util test to no longer include label by default, change debugger to console.err in path-help, remove dynamic ui auth methods from tab count test * properly log errors to the console * capitalize This value is sensitive... * get rid of extra padding on bottom of fieldgroupshow * make auth methods clickable and use new confirm ux * Update sdk/framework/path.go Co-Authored-By: Jim Kalafut <jkalafut@hashicorp.com> * Update sdk/framework/path.go Co-Authored-By: Jim Kalafut <jkalafut@hashicorp.com> * add whitespace * return intErr instead of err * uncomment out helpUrl because we need it * remove extra box class * use const instead of let * remove extra conditional since we already split the pathName later on * ensure we request the correct url when listing generated items * use const * link to list and show pages * remove dead code * show nested item name instead of id * add comments * show tooltip for text-file inputs * fix storybook * remove extra filter * add TODOs * add comments * comment out unused variables but leave them in function signature * only link to auth methods that can be fully managed in the ui * clean up comments * only render tooltip if there is helpText * rename id authMethodPath * remove optionsForQuery since we don't need it * add indentation * standardize ConfirmMessage and show model name instead of id when editing * standardize ConfirmMessage and show model name instead of id when editing * add comments * post to the correct updateUrl so we can edit users and groups * use pop instead of slice * add TODO for finding a better way to store ids * ensure ids are handled the same way on list and show pages; fix editing and deleting * add comment about difference between list and show urls * use model.id instead of name since we do not need it * remove dead code * ensure list pages have page headers * standardize using authMethodPath instead of method and remove dead code * i love indentation * remove more dead code * use new Confirm * show correct flash message when deleting an item * update flash message for creating and updating * use plus icon for creating group/user instead of an arrow
2019-10-17 23:19:14 +00:00
// ItemType is the type of item this path operates on
ItemType string `json:"itemType,omitempty"`
// Group is the suggested UI group to place this field in.
Group string `json:"group,omitempty"`
// Action is the verb to use for the operation.
Action string `json:"action,omitempty"`
Enable generated items for more auth methods (#7513) * enable auth method item configuration in go code * properly parse and list generated items * make sure we only set name on attrs if a label comes from openAPI * correctly construct paths object for method index route * set sensitive property on password for userpass * remove debugger statements * pass method model to list route template to use paths on model for tabs * update tab generation in generated item list, undo enabling userpass users * enable openapi generated itams for certs and userpass, update ldap to no longer have action on list endpoint * add editType to DisplayAttributes, pull tokenutil fields into field group * show sensitive message for sensitive fields displayed in fieldGroupShow component * grab sensitive and editType fields from displayAttrs in openapi-to-attrs util * make sure we don't ask for paths for secret backends since that isn't setup yet * fix styling of sensitive text for fieldGroupShow component * update openapi-to-attrs util test to no longer include label by default, change debugger to console.err in path-help, remove dynamic ui auth methods from tab count test * properly log errors to the console * capitalize This value is sensitive... * get rid of extra padding on bottom of fieldgroupshow * make auth methods clickable and use new confirm ux * Update sdk/framework/path.go Co-Authored-By: Jim Kalafut <jkalafut@hashicorp.com> * Update sdk/framework/path.go Co-Authored-By: Jim Kalafut <jkalafut@hashicorp.com> * add whitespace * return intErr instead of err * uncomment out helpUrl because we need it * remove extra box class * use const instead of let * remove extra conditional since we already split the pathName later on * ensure we request the correct url when listing generated items * use const * link to list and show pages * remove dead code * show nested item name instead of id * add comments * show tooltip for text-file inputs * fix storybook * remove extra filter * add TODOs * add comments * comment out unused variables but leave them in function signature * only link to auth methods that can be fully managed in the ui * clean up comments * only render tooltip if there is helpText * rename id authMethodPath * remove optionsForQuery since we don't need it * add indentation * standardize ConfirmMessage and show model name instead of id when editing * standardize ConfirmMessage and show model name instead of id when editing * add comments * post to the correct updateUrl so we can edit users and groups * use pop instead of slice * add TODO for finding a better way to store ids * ensure ids are handled the same way on list and show pages; fix editing and deleting * add comment about difference between list and show urls * use model.id instead of name since we do not need it * remove dead code * ensure list pages have page headers * standardize using authMethodPath instead of method and remove dead code * i love indentation * remove more dead code * use new Confirm * show correct flash message when deleting an item * update flash message for creating and updating * use plus icon for creating group/user instead of an arrow
2019-10-17 23:19:14 +00:00
// OperationPrefix is a hyphenated lower-case string used to construct
// OpenAPI OperationID (prefix + verb + suffix). OperationPrefix is
// typically a human-readable name of the plugin or a prefix shared by
// multiple related endpoints.
OperationPrefix string `json:"operationPrefix,omitempty"`
// OperationVerb is a hyphenated lower-case string used to construct
// OpenAPI OperationID (prefix + verb + suffix). OperationVerb is typically
// an action to be performed (e.g. "generate", "sign", "login", etc.). If
// not specified, the verb defaults to `logical.Operation.String()`
// (e.g. "read", "list", "delete", "write" for Create/Update)
OperationVerb string `json:"operationVerb,omitempty"`
// OperationSuffix is a hyphenated lower-case string used to construct
// OpenAPI OperationID (prefix + verb + suffix). It is typically the name
// of the resource on which the action is performed (e.g. "role",
// "credentials", etc.). A pipe (|) separator can be used to list different
// suffixes for various permutations of the `Path.Pattern` regular
// expression. If not specified, the suffix defaults to the `Path.Pattern`
// split by dashes.
OperationSuffix string `json:"operationSuffix,omitempty"`
// EditType is the optional type of form field needed for a property
// This is only necessary for a "textarea" or "file"
Enable generated items for more auth methods (#7513) * enable auth method item configuration in go code * properly parse and list generated items * make sure we only set name on attrs if a label comes from openAPI * correctly construct paths object for method index route * set sensitive property on password for userpass * remove debugger statements * pass method model to list route template to use paths on model for tabs * update tab generation in generated item list, undo enabling userpass users * enable openapi generated itams for certs and userpass, update ldap to no longer have action on list endpoint * add editType to DisplayAttributes, pull tokenutil fields into field group * show sensitive message for sensitive fields displayed in fieldGroupShow component * grab sensitive and editType fields from displayAttrs in openapi-to-attrs util * make sure we don't ask for paths for secret backends since that isn't setup yet * fix styling of sensitive text for fieldGroupShow component * update openapi-to-attrs util test to no longer include label by default, change debugger to console.err in path-help, remove dynamic ui auth methods from tab count test * properly log errors to the console * capitalize This value is sensitive... * get rid of extra padding on bottom of fieldgroupshow * make auth methods clickable and use new confirm ux * Update sdk/framework/path.go Co-Authored-By: Jim Kalafut <jkalafut@hashicorp.com> * Update sdk/framework/path.go Co-Authored-By: Jim Kalafut <jkalafut@hashicorp.com> * add whitespace * return intErr instead of err * uncomment out helpUrl because we need it * remove extra box class * use const instead of let * remove extra conditional since we already split the pathName later on * ensure we request the correct url when listing generated items * use const * link to list and show pages * remove dead code * show nested item name instead of id * add comments * show tooltip for text-file inputs * fix storybook * remove extra filter * add TODOs * add comments * comment out unused variables but leave them in function signature * only link to auth methods that can be fully managed in the ui * clean up comments * only render tooltip if there is helpText * rename id authMethodPath * remove optionsForQuery since we don't need it * add indentation * standardize ConfirmMessage and show model name instead of id when editing * standardize ConfirmMessage and show model name instead of id when editing * add comments * post to the correct updateUrl so we can edit users and groups * use pop instead of slice * add TODO for finding a better way to store ids * ensure ids are handled the same way on list and show pages; fix editing and deleting * add comment about difference between list and show urls * use model.id instead of name since we do not need it * remove dead code * ensure list pages have page headers * standardize using authMethodPath instead of method and remove dead code * i love indentation * remove more dead code * use new Confirm * show correct flash message when deleting an item * update flash message for creating and updating * use plus icon for creating group/user instead of an arrow
2019-10-17 23:19:14 +00:00
EditType string `json:"editType,omitempty"`
}
// RequestExample is example of request data.
type RequestExample struct {
Description string // optional description of the request
Data map[string]interface{} // map version of sample JSON request data
// Optional example response to the sample request. This approach is considered
// provisional for now, and this field may be changed or removed.
Response *Response
}
// Response describes and optional demonstrations an operation response.
type Response struct {
Description string // summary of the the response and should always be provided
MediaType string // media type of the response, defaulting to "application/json" if empty
Fields map[string]*FieldSchema // the fields present in this response, used to generate openapi response
Example *logical.Response // example response data
}
// PathOperation is a concrete implementation of OperationHandler.
type PathOperation struct {
Callback OperationFunc
Summary string
Description string
Examples []RequestExample
Responses map[int][]Response
Unpublished bool
Deprecated bool
ForwardPerformanceSecondary bool
ForwardPerformanceStandby bool
DisplayAttrs *DisplayAttributes
}
func (p *PathOperation) Handler() OperationFunc {
return p.Callback
}
func (p *PathOperation) Properties() OperationProperties {
return OperationProperties{
Summary: strings.TrimSpace(p.Summary),
Description: strings.TrimSpace(p.Description),
Responses: p.Responses,
Examples: p.Examples,
Unpublished: p.Unpublished,
Deprecated: p.Deprecated,
ForwardPerformanceSecondary: p.ForwardPerformanceSecondary,
ForwardPerformanceStandby: p.ForwardPerformanceStandby,
DisplayAttrs: p.DisplayAttrs,
}
}
func (p *Path) helpCallback(b *Backend) OperationFunc {
return func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
var tplData pathTemplateData
tplData.Request = req.Path
tplData.RoutePattern = p.Pattern
tplData.Synopsis = strings.TrimSpace(p.HelpSynopsis)
if tplData.Synopsis == "" {
tplData.Synopsis = "<no synopsis>"
}
tplData.Description = strings.TrimSpace(p.HelpDescription)
if tplData.Description == "" {
tplData.Description = "<no description>"
}
// Alphabetize the fields
fieldKeys := make([]string, 0, len(p.Fields))
for k := range p.Fields {
fieldKeys = append(fieldKeys, k)
}
sort.Strings(fieldKeys)
// Build the field help
tplData.Fields = make([]pathTemplateFieldData, len(fieldKeys))
for i, k := range fieldKeys {
schema := p.Fields[k]
description := strings.TrimSpace(schema.Description)
if description == "" {
description = "<no description>"
}
tplData.Fields[i] = pathTemplateFieldData{
Key: k,
Type: schema.Type.String(),
Description: description,
Deprecated: schema.Deprecated,
}
}
help, err := executeTemplate(pathHelpTemplate, &tplData)
if err != nil {
return nil, errwrap.Wrapf("error executing template: {{err}}", err)
}
// The plugin type (e.g. "kv", "cubbyhole") is only assigned at the time
// the plugin is enabled (mounted). If specified in the request, the type
// will be used as part of the request/response names in the OAS document
var requestResponsePrefix string
if v, ok := req.Data["requestResponsePrefix"]; ok {
requestResponsePrefix = v.(string)
}
// Build OpenAPI response for this path
vaultVersion := "unknown"
if b.System() != nil {
// b.System() should always be non-nil, except tests might create a
// Backend without one.
env, err := b.System().PluginEnv(context.Background())
if err != nil {
return nil, err
}
if env != nil {
vaultVersion = env.VaultVersion
}
}
doc := NewOASDocument(vaultVersion)
OpenAPI `generic_mount_paths` follow-up (#18663) * OpenAPI `generic_mount_paths` follow-up An incremental improvement within larger context discussed in #18560. * Following the revert in #18617, re-introduce the change from `{mountPath}` to `{<path-of-mount>_mount_path}`; this is needed, as otherwise paths from multiple plugins would clash - e.g. almost every auth method would provide a conflicting definition for `auth/{mountPath}/login`, and the last one written into the map would win. * Move the half of the functionality that was in `sdk/framework/` to `vault/logical_system.go` with the rest; this is needed, as `sdk/framework/` gets compiled in to externally built plugins, and therefore there may be version skew between it and the Vault main code. Implementing the `generic_mount_paths` feature entirely on one side of this boundary frees us from problems caused by this. * Update the special exception that recognizes `system` and `identity` as singleton mounts to also include the other two singleton mounts, `cubbyhole` and `auth/token`. * Include a comment that documents to restricted circumstances in which the `generic_mount_paths` option makes sense to use: // Note that for this to actually be useful, you have to be using it with // a Vault instance in which you have mounted one of each secrets engine // and auth method of types you are interested in, at paths which identify // their type, and for the KV secrets engine you will probably want to // mount separate kv-v1 and kv-v2 mounts to include the documentation for // each of those APIs. * Fix tests Also remove comment "// TODO update after kv repo update" which was added 4 years ago in #5687 - the implied update has not happened. * Add changelog * Update 18663.txt
2023-01-18 04:07:11 +00:00
if err := documentPath(p, b.SpecialPaths(), requestResponsePrefix, b.BackendType, doc); err != nil {
b.Logger().Warn("error generating OpenAPI", "error", err)
}
return logical.HelpResponse(help, nil, doc), nil
}
}
type pathTemplateData struct {
Request string
RoutePattern string
Synopsis string
Description string
Fields []pathTemplateFieldData
}
type pathTemplateFieldData struct {
Key string
Type string
Deprecated bool
Description string
URL bool
}
const pathHelpTemplate = `
Request: {{.Request}}
Matching Route: {{.RoutePattern}}
{{.Synopsis}}
2016-04-13 21:15:06 +00:00
{{ if .Fields -}}
## PARAMETERS
{{range .Fields}}
{{indent 4 .Key}} ({{.Type}})
{{if .Deprecated}}
{{printf "(DEPRECATED) %s" .Description | indent 8}}
{{else}}
{{indent 8 .Description}}
{{end}}{{end}}{{end}}
## DESCRIPTION
{{.Description}}
`