open-vault/sdk/framework/path.go

349 lines
12 KiB
Go
Raw Normal View History

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.
Pattern string
// Fields is the mapping of data fields to a schema describing that
// field. Named captures in the Pattern also map to fields. If a named
// capture name matches a PUT body name, the named capture takes
// priority.
//
// Note that only named capture fields are available in every operation,
2016-05-15 16:58:36 +00:00
// whereas all fields are available in the Write operation.
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
}
// 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"`
// 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
// 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
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
}
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,
}
}
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)
}
// Build OpenAPI response for this path
doc := NewOASDocument()
if err := documentPath(p, b.SpecialPaths(), 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}}
`