From 4bc8bcda2c268531a7412bb19e9d9b37d7e9f725 Mon Sep 17 00:00:00 2001 From: Jim Kalafut Date: Tue, 6 Nov 2018 10:09:06 -0800 Subject: [PATCH] Update sys path definitions for OpenAPI (#5687) --- vault/logical_cubbyhole.go | 51 +++- vault/logical_system.go | 23 +- vault/logical_system_paths.go | 552 +++++++++++++++++++++++++++++----- vault/logical_system_test.go | 4 +- 4 files changed, 534 insertions(+), 96 deletions(-) diff --git a/vault/logical_cubbyhole.go b/vault/logical_cubbyhole.go index fe4670ed0d..5f45d9e555 100644 --- a/vault/logical_cubbyhole.go +++ b/vault/logical_cubbyhole.go @@ -43,14 +43,36 @@ type CubbyholeBackend struct { func (b *CubbyholeBackend) paths() []*framework.Path { return []*framework.Path{ { - Pattern: ".*", + Pattern: framework.MatchAllRegex("path"), - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleRead, - logical.CreateOperation: b.handleWrite, - logical.UpdateOperation: b.handleWrite, - logical.DeleteOperation: b.handleDelete, - logical.ListOperation: b.handleList, + Fields: map[string]*framework.FieldSchema{ + "path": { + Type: framework.TypeString, + Description: "Specifies the path of the secret.", + }, + }, + + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handleRead, + Summary: "Retrieve the secret at the specified location.", + }, + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleWrite, + Summary: "Store a secret at the specified location.", + }, + logical.CreateOperation: &framework.PathOperation{ + Callback: b.handleWrite, + }, + logical.DeleteOperation: &framework.PathOperation{ + Callback: b.handleDelete, + Summary: "Deletes the secret at the specified location.", + }, + logical.ListOperation: &framework.PathOperation{ + Callback: b.handleList, + Summary: "List secret entries at the specified location.", + Description: "Folders are suffixed with /. The input must be a folder; list on a file will not return a value. The values themselves are not accessible via this command.", + }, }, ExistenceCheck: b.handleExistenceCheck, @@ -87,8 +109,10 @@ func (b *CubbyholeBackend) handleRead(ctx context.Context, req *logical.Request, return nil, fmt.Errorf("client token empty") } + path := data.Get("path").(string) + // Read the path - out, err := req.Storage.Get(ctx, req.ClientToken+"/"+req.Path) + out, err := req.Storage.Get(ctx, req.ClientToken+"/"+path) if err != nil { return nil, errwrap.Wrapf("read failed: {{err}}", err) } @@ -121,6 +145,8 @@ func (b *CubbyholeBackend) handleWrite(ctx context.Context, req *logical.Request return nil, fmt.Errorf("missing data fields") } + path := data.Get("path").(string) + // JSON encode the data buf, err := json.Marshal(req.Data) if err != nil { @@ -129,7 +155,7 @@ func (b *CubbyholeBackend) handleWrite(ctx context.Context, req *logical.Request // Write out a new key entry := &logical.StorageEntry{ - Key: req.ClientToken + "/" + req.Path, + Key: req.ClientToken + "/" + path, Value: buf, } if req.WrapInfo != nil && req.WrapInfo.SealWrap { @@ -146,8 +172,11 @@ func (b *CubbyholeBackend) handleDelete(ctx context.Context, req *logical.Reques if req.ClientToken == "" { return nil, fmt.Errorf("client token empty") } + + path := data.Get("path").(string) + // Delete the key at the request path - if err := req.Storage.Delete(ctx, req.ClientToken+"/"+req.Path); err != nil { + if err := req.Storage.Delete(ctx, req.ClientToken+"/"+path); err != nil { return nil, err } @@ -162,7 +191,7 @@ func (b *CubbyholeBackend) handleList(ctx context.Context, req *logical.Request, // Right now we only handle directories, so ensure it ends with / We also // check if it's empty so we don't end up doing a listing on '//' - path := req.Path + path := data.Get("path").(string) if path != "" && !strings.HasSuffix(path, "/") { path = path + "/" } diff --git a/vault/logical_system.go b/vault/logical_system.go index 198cb06cd8..3b7b7b00af 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -158,12 +158,25 @@ func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleRawRead, - logical.UpdateOperation: b.handleRawWrite, - logical.DeleteOperation: b.handleRawDelete, - logical.ListOperation: b.handleRawList, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handleRawRead, + Summary: "Read the value of the key at the given path.", + }, + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleRawWrite, + Summary: "Update the value of the key at the given path.", + }, + logical.DeleteOperation: &framework.PathOperation{ + Callback: b.handleRawDelete, + Summary: "Delete the key with given path.", + }, + logical.ListOperation: &framework.PathOperation{ + Callback: b.handleRawList, + Summary: "Return a list keys for a given path prefix.", + }, }, + HelpSynopsis: strings.TrimSpace(sysHelp["raw"][0]), HelpDescription: strings.TrimSpace(sysHelp["raw"][1]), }) diff --git a/vault/logical_system_paths.go b/vault/logical_system_paths.go index c010d86b6e..dea1f5ad2d 100644 --- a/vault/logical_system_paths.go +++ b/vault/logical_system_paths.go @@ -27,10 +27,21 @@ func (b *SystemBackend) configPaths() []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleCORSRead, - logical.UpdateOperation: b.handleCORSUpdate, - logical.DeleteOperation: b.handleCORSDelete, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handleCORSRead, + Summary: "Return the current CORS settings.", + Description: "", + }, + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleCORSUpdate, + Summary: "Configure the CORS settings.", + Description: "", + }, + logical.DeleteOperation: &framework.PathOperation{ + Callback: b.handleCORSDelete, + Summary: "Remove any CORS settings.", + }, }, HelpDescription: strings.TrimSpace(sysHelp["config/cors"][0]), @@ -51,10 +62,19 @@ func (b *SystemBackend) configPaths() []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleConfigUIHeadersRead, - logical.UpdateOperation: b.handleConfigUIHeadersUpdate, - logical.DeleteOperation: b.handleConfigUIHeadersDelete, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handleConfigUIHeadersRead, + Summary: "Return the given UI header's configuration", + }, + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleConfigUIHeadersUpdate, + Summary: "Configure the values to be returned for the UI header.", + }, + logical.DeleteOperation: &framework.PathOperation{ + Callback: b.handleConfigUIHeadersDelete, + Summary: "Remove a UI header.", + }, }, HelpDescription: strings.TrimSpace(sysHelp["config/ui/headers"][0]), @@ -64,8 +84,11 @@ func (b *SystemBackend) configPaths() []*framework.Path { { Pattern: "config/ui/headers/$", - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ListOperation: b.handleConfigUIHeadersList, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ListOperation: &framework.PathOperation{ + Callback: b.handleConfigUIHeadersList, + Summary: "Return a list of configured UI headers.", + }, }, HelpDescription: strings.TrimSpace(sysHelp["config/ui/headers"][0]), @@ -73,29 +96,200 @@ func (b *SystemBackend) configPaths() []*framework.Path { }, { - Pattern: "generate-root(/attempt)?$", + Pattern: "generate-root(/attempt)?$", + Fields: map[string]*framework.FieldSchema{ + "pgp_key": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Specifies a base64-encoded PGP public key.", + }, + }, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Summary: "Read the configuration and progress of the current root generation attempt.", + }, + logical.UpdateOperation: &framework.PathOperation{ + Summary: "Initializes a new root generation attempt.", + Description: "Only a single root generation attempt can take place at a time. One (and only one) of otp or pgp_key are required.", + }, + logical.DeleteOperation: &framework.PathOperation{ + Summary: "Cancels any in-progress root generation attempt.", + }, + }, + HelpSynopsis: strings.TrimSpace(sysHelp["generate-root"][0]), HelpDescription: strings.TrimSpace(sysHelp["generate-root"][1]), }, + { + Pattern: "generate-root/update$", + Fields: map[string]*framework.FieldSchema{ + "key": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Specifies a single master key share.", + }, + "nonce": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Specifies the nonce of the attempt.", + }, + }, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Summary: "Enter a single master key share to progress the root generation attempt.", + Description: "If the threshold number of master key shares is reached, Vault will complete the root generation and issue the new token. Otherwise, this API must be called multiple times until that threshold is met. The attempt nonce must be provided with each call.", + }, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["generate-root"][0]), + HelpDescription: strings.TrimSpace(sysHelp["generate-root"][1]), + }, + { + Pattern: "health$", + + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Summary: "Returns the health status of Vault.", + Responses: map[int][]framework.Response{ + 200: {{Description: "initialized, unsealed, and active"}}, + 429: {{Description: "unsealed and standby"}}, + 472: {{Description: "data recovery mode replication secondary and active"}}, + 501: {{Description: "not initialized"}}, + 503: {{Description: "sealed"}}, + }, + }, + }, + }, { - Pattern: "init$", + Pattern: "init$", + Fields: map[string]*framework.FieldSchema{ + "pgp_keys": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: "Specifies an array of PGP public keys used to encrypt the output unseal keys. Ordering is preserved. The keys must be base64-encoded from their original binary representation. The size of this array must be the same as `secret_shares`.", + }, + "root_token_pgp_key": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Specifies a PGP public key used to encrypt the initial root token. The key must be base64-encoded from its original binary representation.", + }, + "secret_shares": &framework.FieldSchema{ + Type: framework.TypeInt, + Description: "Specifies the number of shares to split the master key into.", + }, + "secret_threshold": &framework.FieldSchema{ + Type: framework.TypeInt, + Description: "Specifies the number of shares required to reconstruct the master key. This must be less than or equal secret_shares. If using Vault HSM with auto-unsealing, this value must be the same as `secret_shares`.", + }, + "stored_shares": &framework.FieldSchema{ + Type: framework.TypeInt, + Description: "Specifies the number of shares that should be encrypted by the HSM and stored for auto-unsealing. Currently must be the same as `secret_shares`.", + }, + "recovery_shares": &framework.FieldSchema{ + Type: framework.TypeInt, + Description: "Specifies the number of shares to split the recovery key into.", + }, + "recovery_threshold": &framework.FieldSchema{ + Type: framework.TypeInt, + Description: " Specifies the number of shares required to reconstruct the recovery key. This must be less than or equal to `recovery_shares`.", + }, + "recovery_pgp_keys": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: "Specifies an array of PGP public keys used to encrypt the output recovery keys. Ordering is preserved. The keys must be base64-encoded from their original binary representation. The size of this array must be the same as `recovery_shares`.", + }, + }, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Summary: "Returns the initialization status of Vault.", + }, + logical.UpdateOperation: &framework.PathOperation{ + Summary: "Initialize a new Vault.", + Description: "The Vault must not have been previously initialized. The recovery options, as well as the stored shares option, are only available when using Vault HSM.", + }, + }, + HelpSynopsis: strings.TrimSpace(sysHelp["init"][0]), HelpDescription: strings.TrimSpace(sysHelp["init"][1]), }, + { + Pattern: "leader$", + + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Summary: "Returns the high availability status and current leader instance of Vault.", + }, + }, + + HelpSynopsis: "Check the high availability status and current leader of Vault", + }, + { + Pattern: "step-down$", + + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Summary: "Cause the node to give up active status.", + Description: "This endpoint forces the node to give up active status. If the node does not have active status, this endpoint does nothing. Note that the node will sleep for ten seconds before attempting to grab the active lock again, but if no standby nodes grab the active lock in the interim, the same node may become the active node again.", + Responses: map[int][]framework.Response{ + 204: {{Description: "empty body"}}, + }, + }, + }, + }, } } func (b *SystemBackend) rekeyPaths() []*framework.Path { return []*framework.Path{ + { + Pattern: "rekey/init", + + Fields: map[string]*framework.FieldSchema{ + "secret_shares": &framework.FieldSchema{ + Type: framework.TypeInt, + Description: "Specifies the number of shares to split the master key into.", + }, + "secret_threshold": &framework.FieldSchema{ + Type: framework.TypeInt, + Description: "Specifies the number of shares required to reconstruct the master key. This must be less than or equal secret_shares. If using Vault HSM with auto-unsealing, this value must be the same as secret_shares.", + }, + "pgp_keys": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: "Specifies an array of PGP public keys used to encrypt the output unseal keys. Ordering is preserved. The keys must be base64-encoded from their original binary representation. The size of this array must be the same as secret_shares.", + }, + "backup": &framework.FieldSchema{ + Type: framework.TypeBool, + Description: "Specifies if using PGP-encrypted keys, whether Vault should also store a plaintext backup of the PGP-encrypted keys.", + }, + "require_verification": &framework.FieldSchema{ + Type: framework.TypeBool, + Description: "Turns on verification functionality", + }, + }, + + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Summary: "Reads the configuration and progress of the current rekey attempt.", + }, + logical.UpdateOperation: &framework.PathOperation{ + 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{ + 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.", + }, + }, + }, { Pattern: "rekey/backup$", Fields: map[string]*framework.FieldSchema{}, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleRekeyRetrieveBarrier, - logical.DeleteOperation: b.handleRekeyDeleteBarrier, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handleRekeyRetrieveBarrier, + 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.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["rekey_backup"][0]), @@ -115,21 +309,96 @@ func (b *SystemBackend) rekeyPaths() []*framework.Path { HelpSynopsis: strings.TrimSpace(sysHelp["rekey_backup"][0]), HelpDescription: strings.TrimSpace(sysHelp["rekey_backup"][0]), }, + { + Pattern: "rekey/update", + + Fields: map[string]*framework.FieldSchema{ + "key": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Specifies a single master key share.", + }, + "nonce": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Specifies the nonce of the rekey attempt.", + }, + }, + + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Summary: "Enter a single master key share to progress the rekey of the Vault.", + }, + }, + }, + { + Pattern: "rekey/verify", + + Fields: map[string]*framework.FieldSchema{ + "key": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Specifies a single master share key from the new set of shares.", + }, + "nonce": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Specifies the nonce of the rekey verification operation.", + }, + }, + + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Summary: "Read the configuration and progress of the current rekey verification attempt.", + }, + logical.DeleteOperation: &framework.PathOperation{ + 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{ + Summary: "Enter a single new key share to progress the rekey verification operation.", + }, + }, + }, { - Pattern: "seal-status$", + Pattern: "seal-status$", + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Summary: "Check the seal status of a Vault.", + }, + }, + HelpSynopsis: strings.TrimSpace(sysHelp["seal-status"][0]), HelpDescription: strings.TrimSpace(sysHelp["seal-status"][1]), }, { - Pattern: "seal$", + Pattern: "seal$", + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Summary: "Seal the Vault.", + }, + }, HelpSynopsis: strings.TrimSpace(sysHelp["seal"][0]), HelpDescription: strings.TrimSpace(sysHelp["seal"][1]), }, { - Pattern: "unseal$", + Pattern: "unseal$", + Fields: map[string]*framework.FieldSchema{ + "key": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Specifies a single master key share. This is required unless reset is true.", + }, + "reset": &framework.FieldSchema{ + Type: framework.TypeBool, + Description: "Specifies if previously-provided unseal keys are discarded and the unseal process is reset.", + }, + }, + + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Summary: "Unseal the Vault.", + }, + }, + HelpSynopsis: strings.TrimSpace(sysHelp["unseal"][0]), HelpDescription: strings.TrimSpace(sysHelp["unseal"][1]), }, @@ -163,8 +432,11 @@ func (b *SystemBackend) auditPaths() []*framework.Path { { Pattern: "audit$", - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleAuditTable, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handleAuditTable, + Summary: "List the enabled audit devices.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["audit-table"][0]), @@ -198,9 +470,15 @@ func (b *SystemBackend) auditPaths() []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleEnableAudit, - logical.DeleteOperation: b.handleDisableAudit, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleEnableAudit, + Summary: "Enable a new audit device at the supplied path.", + }, + logical.DeleteOperation: &framework.PathOperation{ + Callback: b.handleDisableAudit, + Summary: "Disable the audit device at the given path.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["audit"][0]), @@ -219,10 +497,19 @@ func (b *SystemBackend) auditPaths() []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleAuditedHeaderUpdate, - logical.DeleteOperation: b.handleAuditedHeaderDelete, - logical.ReadOperation: b.handleAuditedHeaderRead, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleAuditedHeaderUpdate, + Summary: "Enable auditing of a header.", + }, + logical.DeleteOperation: &framework.PathOperation{ + Callback: b.handleAuditedHeaderDelete, + Summary: "Disable auditing of the given request header.", + }, + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handleAuditedHeaderRead, + Summary: "List the information for the given request header.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["audited-headers-name"][0]), @@ -232,8 +519,11 @@ func (b *SystemBackend) auditPaths() []*framework.Path { { Pattern: "config/auditing/request-headers$", - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleAuditedHeadersRead, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handleAuditedHeadersRead, + Summary: "List the request headers that are configured to be audited.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["audited-headers"][0]), @@ -299,10 +589,19 @@ func (b *SystemBackend) pluginsCatalogPath() *framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handlePluginCatalogUpdate, - logical.DeleteOperation: b.handlePluginCatalogDelete, - logical.ReadOperation: b.handlePluginCatalogRead, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handlePluginCatalogUpdate, + Summary: "Register a new plugin, or updates an existing one with the supplied name.", + }, + logical.DeleteOperation: &framework.PathOperation{ + Callback: b.handlePluginCatalogDelete, + Summary: "Remove the plugin with the given name.", + }, + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handlePluginCatalogRead, + Summary: "Return the configuration data for the plugin with the given name.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["plugin-catalog"][0]), @@ -325,8 +624,12 @@ func (b *SystemBackend) pluginsReloadPath() *framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handlePluginReloadUpdate, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handlePluginReloadUpdate, + Summary: "Reload mounted plugin backends.", + Description: "Either the plugin name (`plugin`) or the desired plugin backend mounts (`mounts`) must be provided, but not both. In the case that the plugin name is provided, all mounted paths that use that plugin backend will be reloaded.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["plugin-reload"][0]), @@ -340,8 +643,11 @@ func (b *SystemBackend) pluginsCatalogListPath() *framework.Path { Fields: map[string]*framework.FieldSchema{}, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ListOperation: b.handlePluginCatalogList, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ListOperation: &framework.PathOperation{ + Callback: b.handlePluginCatalogList, + Summary: "List the plugins in the catalog.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["plugin-catalog"][0]), @@ -431,10 +737,22 @@ func (b *SystemBackend) internalPaths() []*framework.Path { logical.ReadOperation: b.pathInternalOpenAPI, }, }, + { + Pattern: "internal/specs/openapi", + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.pathInternalOpenAPI, + Summary: "Generate an OpenAPI 3 document of all mounted paths.", + }, + }, + }, { Pattern: "internal/ui/mounts", - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.pathInternalUIMountsRead, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.pathInternalUIMountsRead, + Summary: "Lists all enabled and visible auth and secrets mounts.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["internal-ui-mounts"][0]), HelpDescription: strings.TrimSpace(sysHelp["internal-ui-mounts"][1]), @@ -447,24 +765,33 @@ func (b *SystemBackend) internalPaths() []*framework.Path { Description: "The path of the mount.", }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.pathInternalUIMountRead, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.pathInternalUIMountRead, + Summary: "Return information about the given mount.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["internal-ui-mounts"][0]), HelpDescription: strings.TrimSpace(sysHelp["internal-ui-mounts"][1]), }, { Pattern: "internal/ui/namespaces", - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: pathInternalUINamespacesRead(b), + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: pathInternalUINamespacesRead(b), + Unpublished: true, + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["internal-ui-namespaces"][0]), HelpDescription: strings.TrimSpace(sysHelp["internal-ui-namespaces"][1]), }, { Pattern: "internal/ui/resultant-acl", - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.pathInternalUIResultantACL, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.pathInternalUIResultantACL, + Unpublished: true, + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["internal-ui-resultant-acl"][0]), HelpDescription: strings.TrimSpace(sysHelp["internal-ui-resultant-acl"][1]), @@ -485,6 +812,7 @@ func (b *SystemBackend) capabilitiesPaths() []*framework.Path { "path": &framework.FieldSchema{ Type: framework.TypeCommaStringSlice, Description: "(DEPRECATED) Path on which capabilities are being queried. Use 'paths' instead.", + Deprecated: true, }, "paths": &framework.FieldSchema{ Type: framework.TypeCommaStringSlice, @@ -511,6 +839,7 @@ func (b *SystemBackend) capabilitiesPaths() []*framework.Path { "path": &framework.FieldSchema{ Type: framework.TypeCommaStringSlice, Description: "(DEPRECATED) Path on which capabilities are being queried. Use 'paths' instead.", + Deprecated: true, }, "paths": &framework.FieldSchema{ Type: framework.TypeCommaStringSlice, @@ -537,6 +866,7 @@ func (b *SystemBackend) capabilitiesPaths() []*framework.Path { "path": &framework.FieldSchema{ Type: framework.TypeCommaStringSlice, Description: "(DEPRECATED) Path on which capabilities are being queried. Use 'paths' instead.", + Deprecated: true, }, "paths": &framework.FieldSchema{ Type: framework.TypeCommaStringSlice, @@ -566,8 +896,11 @@ func (b *SystemBackend) leasePaths() []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ListOperation: b.handleLeaseLookupList, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ListOperation: &framework.PathOperation{ + Callback: b.handleLeaseLookupList, + Summary: "Returns a list of lease ids.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["leases"][0]), @@ -584,8 +917,11 @@ func (b *SystemBackend) leasePaths() []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleLeaseLookup, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleLeaseLookup, + Summary: "Retrieve lease metadata.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["leases"][0]), @@ -610,8 +946,11 @@ func (b *SystemBackend) leasePaths() []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleRenew, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleRenew, + Summary: "Renews a lease, requesting to extend the lease.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["renew"][0]), @@ -637,8 +976,11 @@ func (b *SystemBackend) leasePaths() []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleRevoke, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleRevoke, + Summary: "Revokes a lease immediately.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["revoke"][0]), @@ -655,8 +997,12 @@ func (b *SystemBackend) leasePaths() []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleRevokeForce, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleRevokeForce, + Summary: "Revokes all secrets or tokens generated under a given prefix immediately", + Description: "Unlike `/sys/leases/revoke-prefix`, this path ignores backend errors encountered during revocation. This is potentially very dangerous and should only be used in specific emergency situations where errors in the backend or the connected backend service prevent normal revocation.\n\nBy ignoring these errors, Vault abdicates responsibility for ensuring that the issued credentials or secrets are properly revoked and/or cleaned up. Access to this endpoint should be tightly controlled.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["revoke-force"][0]), @@ -678,8 +1024,11 @@ func (b *SystemBackend) leasePaths() []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleRevokePrefix, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleRevokePrefix, + Summary: "Revokes all secrets (via a lease ID prefix) or tokens (via the tokens' path property) generated under a given prefix immediately.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["revoke-prefix"][0]), @@ -773,9 +1122,17 @@ func (b *SystemBackend) authPaths() []*framework.Path { Description: strings.TrimSpace(sysHelp["passthrough_request_headers"][0]), }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleAuthTuneRead, - logical.UpdateOperation: b.handleAuthTuneWrite, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handleAuthTuneRead, + Summary: "Reads the given auth path's configuration.", + Description: "This endpoint requires sudo capability on the final path, but the same functionality can be achieved without sudo via `sys/mounts/auth/[auth-path]/tune`.", + }, + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleAuthTuneWrite, + Summary: "Tune configuration parameters for a given auth path.", + Description: "This endpoint requires sudo capability on the final path, but the same functionality can be achieved without sudo via `sys/mounts/auth/[auth-path]/tune`.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["auth_tune"][0]), HelpDescription: strings.TrimSpace(sysHelp["auth_tune"][1]), @@ -818,9 +1175,18 @@ func (b *SystemBackend) authPaths() []*framework.Path { Description: strings.TrimSpace(sysHelp["auth_options"][0]), }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleEnableAuth, - logical.DeleteOperation: b.handleDisableAuth, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleEnableAuth, + Summary: "Enables a new auth method.", + Description: `After enabling, the auth method can be accessed and configured via the auth path specified as part of the URL. This auth path will be nested under the auth prefix. + +For example, enable the "foo" auth method will make it accessible at /auth/foo.`, + }, + logical.DeleteOperation: &framework.PathOperation{ + Callback: b.handleDisableAuth, + Summary: "Disable the auth method at the given auth path", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["auth"][0]), HelpDescription: strings.TrimSpace(sysHelp["auth"][1]), @@ -853,6 +1219,7 @@ func (b *SystemBackend) policyPaths() []*framework.Path { "rules": &framework.FieldSchema{ Type: framework.TypeString, Description: strings.TrimSpace(sysHelp["policy-rules"][0]), + Deprecated: true, }, "policy": &framework.FieldSchema{ Type: framework.TypeString, @@ -860,10 +1227,19 @@ func (b *SystemBackend) policyPaths() []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handlePoliciesRead(PolicyTypeACL), - logical.UpdateOperation: b.handlePoliciesSet(PolicyTypeACL), - logical.DeleteOperation: b.handlePoliciesDelete(PolicyTypeACL), + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handlePoliciesRead(PolicyTypeACL), + Summary: "Retrieve the policy body for the named policy.", + }, + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handlePoliciesSet(PolicyTypeACL), + Summary: "Add a new or update an existing policy.", + }, + logical.DeleteOperation: &framework.PathOperation{ + Callback: b.handlePoliciesDelete(PolicyTypeACL), + Summary: "Delete the policy with the given name.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["policy"][0]), @@ -895,10 +1271,19 @@ func (b *SystemBackend) policyPaths() []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handlePoliciesRead(PolicyTypeACL), - logical.UpdateOperation: b.handlePoliciesSet(PolicyTypeACL), - logical.DeleteOperation: b.handlePoliciesDelete(PolicyTypeACL), + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handlePoliciesRead(PolicyTypeACL), + Summary: "Retrieve information about the named ACL policy.", + }, + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handlePoliciesSet(PolicyTypeACL), + Summary: "Add a new or update an existing ACL policy.", + }, + logical.DeleteOperation: &framework.PathOperation{ + Callback: b.handlePoliciesDelete(PolicyTypeACL), + Summary: "Delete the ACL policy with the given name.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["policy"][0]), @@ -946,9 +1331,15 @@ func (b *SystemBackend) wrappingPaths() []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleWrappingLookup, - logical.ReadOperation: b.handleWrappingLookup, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleWrappingLookup, + Summary: "Look up wrapping properties for the given token.", + }, + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handleWrappingLookup, + Summary: "Look up wrapping properties for the requester's token.", + }, }, HelpSynopsis: strings.TrimSpace(sysHelp["wraplookup"][0]), @@ -1071,11 +1462,16 @@ func (b *SystemBackend) mountPaths() []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleMount, - logical.DeleteOperation: b.handleUnmount, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleMount, + Summary: "Enable a new secrets engine at the given path.", + }, + logical.DeleteOperation: &framework.PathOperation{ + Callback: b.handleUnmount, + Summary: "Disable the mount point specified at the given path.", + }, }, - HelpSynopsis: strings.TrimSpace(sysHelp["mount"][0]), HelpDescription: strings.TrimSpace(sysHelp["mount"][1]), }, diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index ce05b0819f..dff2175f09 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -2509,9 +2509,9 @@ func TestSystemBackend_OpenAPI(t *testing.T) { tag string }{ {"/auth/token/lookup", "auth"}, - {"/cubbyhole/.*", "secrets"}, // TODO update after sys docs update + {"/cubbyhole/{path}", "secrets"}, {"/identity/group/id", "identity"}, - {"/secret/.*", "secrets"}, // TODO update after sys docs update + {"/secret/.*", "secrets"}, // TODO update after kv repo update {"/sys/policy", "system"}, }