From 020af2fac278d45e2e72223741f1980f6d929a7d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 2 Apr 2015 22:21:33 -0700 Subject: [PATCH] http: help --- api/SPEC.md | 11 +++++++++++ api/help.go | 6 ++++++ http/handler.go | 23 ++++++++++++++++++++++- http/help.go | 40 ++++++++++++++++++++++++++++++++++++++++ http/help_test.go | 27 +++++++++++++++++++++++++++ 5 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 api/help.go create mode 100644 http/help.go create mode 100644 http/help_test.go diff --git a/api/SPEC.md b/api/SPEC.md index 99949fba0..09dd433d1 100644 --- a/api/SPEC.md +++ b/api/SPEC.md @@ -56,6 +56,17 @@ that can be enabled (see Authentication is done with the login endpoint. The login endpoint returns an access token that is set as the `token` cookie. +## Help + +To retrieve the help for any API within Vault, including mounted +backends, credential providers, etc. then append `?help=1` to any +URL. If you have valid permission to access the path, then the help text +will be returned with the following structure: + + { + "help": "help text" + } + ## Error Response A common JSON structure is always returned to return errors: diff --git a/api/help.go b/api/help.go new file mode 100644 index 000000000..62a101105 --- /dev/null +++ b/api/help.go @@ -0,0 +1,6 @@ +package api + +// Help reads the help information for the given path. +func (c *Client) Help(path string) (string, error) { + return "", nil +} diff --git a/http/handler.go b/http/handler.go index 746ed6584..ade250ecc 100644 --- a/http/handler.go +++ b/http/handler.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "strings" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/vault" @@ -15,6 +16,7 @@ const AuthCookieName = "token" // Handler returns an http.Handler for the API. This can be used on // its own to mount the Vault API within another web server. func Handler(core *vault.Core) http.Handler { + // Create the muxer to handle the actual endpoints mux := http.NewServeMux() mux.Handle("/v1/sys/init", handleSysInit(core)) mux.Handle("/v1/sys/seal-status", handleSysSealStatus(core)) @@ -29,7 +31,26 @@ func Handler(core *vault.Core) http.Handler { mux.Handle("/v1/sys/audit", handleSysListAudit(core)) mux.Handle("/v1/sys/audit/", handleSysAudit(core)) mux.Handle("/v1/", handleLogical(core)) - return mux + + // Wrap the handler in another handler to trigger all help paths. + handler := handleHelpHandler(mux, core) + + return handler +} + +// stripPrefix is a helper to strip a prefix from the path. It will +// return false from the second return value if it the prefix doesn't exist. +func stripPrefix(prefix, path string) (string, bool) { + if !strings.HasPrefix(path, prefix) { + return "", false + } + + path = path[len(prefix):] + if path == "" { + return "", false + } + + return path, true } func parseRequest(r *http.Request, out interface{}) error { diff --git a/http/help.go b/http/help.go new file mode 100644 index 000000000..b512da5fc --- /dev/null +++ b/http/help.go @@ -0,0 +1,40 @@ +package http + +import ( + "net/http" + + "github.com/hashicorp/vault/logical" + "github.com/hashicorp/vault/vault" +) + +func handleHelpHandler(h http.Handler, core *vault.Core) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + // If the help parameter is not blank, then show the help + if v := req.URL.Query().Get("help"); v != "" { + handleHelp(core, w, req) + return + } + + h.ServeHTTP(w, req) + return + }) +} + +func handleHelp(core *vault.Core, w http.ResponseWriter, req *http.Request) { + path, ok := stripPrefix("/v1/", req.URL.Path) + if !ok { + respondError(w, http.StatusNotFound, nil) + return + } + + resp, err := core.HandleRequest(requestAuth(req, &logical.Request{ + Operation: logical.HelpOperation, + Path: path, + })) + if err != nil { + respondError(w, http.StatusInternalServerError, err) + return + } + + respondOk(w, resp.Data) +} diff --git a/http/help_test.go b/http/help_test.go new file mode 100644 index 000000000..2f225e4f9 --- /dev/null +++ b/http/help_test.go @@ -0,0 +1,27 @@ +package http + +import ( + "net/http" + "testing" + + "github.com/hashicorp/vault/vault" +) + +func TestHelp(t *testing.T) { + core, _, token := vault.TestCoreUnsealed(t) + ln, addr := TestServer(t, core) + defer ln.Close() + TestServerAuth(t, addr, token) + + resp, err := http.Get(addr + "/v1/sys/mounts?help=1") + if err != nil { + t.Fatalf("err: %s", err) + } + + var actual map[string]interface{} + testResponseStatus(t, resp, 200) + testResponseBody(t, resp, &actual) + if _, ok := actual["help"]; !ok { + t.Fatalf("bad: %#v", actual) + } +}