Added sys/capabililties endpoint
This commit is contained in:
parent
0998e1cdf9
commit
5749a6718c
|
@ -0,0 +1,48 @@
|
|||
package api
|
||||
|
||||
func (c *Sys) CapabilitiesSelf(path string) ([]string, error) {
|
||||
body := map[string]string{
|
||||
"path": path,
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("POST", "/v1/sys/capabilities-self")
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result capabilitiesResp
|
||||
err = resp.DecodeJSON(&result)
|
||||
return result.Capabilities, err
|
||||
}
|
||||
|
||||
func (c *Sys) Capabilities(token, path string) ([]string, error) {
|
||||
body := map[string]string{
|
||||
"token": token,
|
||||
"path": path,
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("POST", "/v1/sys/capabilities")
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result capabilitiesResp
|
||||
err = resp.DecodeJSON(&result)
|
||||
return result.Capabilities, err
|
||||
}
|
||||
|
||||
type capabilitiesResp struct {
|
||||
Capabilities []string `json:"capabilities"`
|
||||
}
|
|
@ -290,6 +290,12 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
|
|||
}, nil
|
||||
},
|
||||
|
||||
"capabilities": func() (cli.Command, error) {
|
||||
return &command.CapabilitiesCommand{
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
|
||||
"version": func() (cli.Command, error) {
|
||||
versionInfo := version.GetVersion()
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CapabilitiesCommand is a Command that enables a new endpoint.
|
||||
type CapabilitiesCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (c *CapabilitiesCommand) Run(args []string) int {
|
||||
flags := c.Meta.FlagSet("capabilities", FlagSetDefault)
|
||||
flags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
args = flags.Args()
|
||||
if len(args) > 2 {
|
||||
flags.Usage()
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"\ncapabilities expects at most two arguments"))
|
||||
return 1
|
||||
}
|
||||
|
||||
var token string
|
||||
var path string
|
||||
switch len(args) {
|
||||
case 1:
|
||||
// only path is provided
|
||||
log.Printf("only path is provided")
|
||||
path = args[0]
|
||||
case 2:
|
||||
// both token and path are provided
|
||||
log.Printf("both token and path are provided")
|
||||
token = args[0]
|
||||
path = args[1]
|
||||
default:
|
||||
}
|
||||
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"Error initializing client: %s", err))
|
||||
return 2
|
||||
}
|
||||
|
||||
log.Printf("vishal: token:'%s' path:'%s'\n", token, path)
|
||||
var capabilities []string
|
||||
if token == "" {
|
||||
capabilities, err = client.Sys().CapabilitiesSelf(path)
|
||||
} else {
|
||||
capabilities, err = client.Sys().Capabilities(token, path)
|
||||
}
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"Error retrieving capabilities: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
c.Ui.Output(fmt.Sprintf("Capabilities: '%s'", capabilities))
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *CapabilitiesCommand) Synopsis() string {
|
||||
return "Fetch the capabilities of a given token on a given path"
|
||||
}
|
||||
|
||||
func (c *CapabilitiesCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: vault capabilities [options] [token] path
|
||||
|
||||
Fetch the capabilities of a token on a given path.
|
||||
If a token is given to the command '/sys/capabilities' will be called with
|
||||
the given token; otherwise '/sys/capabilities-self' will be called with the
|
||||
client token.
|
||||
|
||||
General Options:
|
||||
|
||||
` + generalOptionsUsage()
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
func handleSysCapabilitiesSelf(core *vault.Core) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" && r.Method != "PUT" {
|
||||
respondError(w, http.StatusMethodNotAllowed, nil)
|
||||
return
|
||||
}
|
||||
log.Printf("vishal: handleSysCapabilitiesSelf: r:%#v, r.URL:%s r.URL.Path:%s\n", r, r.URL, r.URL.Path)
|
||||
// Parse the request if we can
|
||||
var req capabilitiesRequest
|
||||
if err := parseRequest(r, &req); err != nil {
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, ok := request(core, w, r, requestAuth(r, &logical.Request{
|
||||
Operation: logical.UpdateOperation,
|
||||
Path: "sys/capabilities-self",
|
||||
// Connection: getConnection(r),
|
||||
Data: map[string]interface{}{
|
||||
"path": req.Path,
|
||||
},
|
||||
}))
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if resp == nil {
|
||||
respondError(w, http.StatusNotFound, nil)
|
||||
return
|
||||
}
|
||||
|
||||
var capabilities []string
|
||||
capabilitiesRaw, ok := resp.Data["keys"]
|
||||
if ok {
|
||||
capabilities = capabilitiesRaw.([]string)
|
||||
}
|
||||
|
||||
respondOk(w, &capabilitiesResponse{Capabilities: capabilities})
|
||||
})
|
||||
}
|
||||
|
||||
func handleSysCapabilities(core *vault.Core) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" && r.Method != "PUT" {
|
||||
respondError(w, http.StatusMethodNotAllowed, nil)
|
||||
return
|
||||
}
|
||||
log.Printf("vishal: handleSysCapabilities: r: %#v\n", r)
|
||||
// Parse the request if we can
|
||||
var req capabilitiesRequest
|
||||
if err := parseRequest(r, &req); err != nil {
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, ok := request(core, w, r, requestAuth(r, &logical.Request{
|
||||
Operation: logical.UpdateOperation,
|
||||
Path: "sys/capabilities",
|
||||
Connection: getConnection(r),
|
||||
Data: map[string]interface{}{
|
||||
"token": req.Token,
|
||||
"path": req.Path,
|
||||
},
|
||||
}))
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if resp == nil {
|
||||
respondError(w, http.StatusNotFound, nil)
|
||||
return
|
||||
}
|
||||
|
||||
var capabilities []string
|
||||
capabilitiesRaw, ok := resp.Data["keys"]
|
||||
if ok {
|
||||
capabilities = capabilitiesRaw.([]string)
|
||||
}
|
||||
|
||||
respondOk(w, &capabilitiesResponse{Capabilities: capabilities})
|
||||
})
|
||||
}
|
||||
|
||||
type capabilitiesResponse struct {
|
||||
Capabilities []string `json:"policies"`
|
||||
}
|
||||
|
||||
type capabilitiesRequest struct {
|
||||
Token string `json:"token"`
|
||||
Path string `json:"path"`
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -277,6 +279,50 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) logical.Backend
|
|||
HelpDescription: strings.TrimSpace(sysHelp["policy"][1]),
|
||||
},
|
||||
|
||||
&framework.Path{
|
||||
Pattern: "capabilities-self",
|
||||
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"token": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Token of which capabilities are being requested",
|
||||
},
|
||||
"path": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Path for which token's capabilities are being fetched",
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.UpdateOperation: b.handleCapabilitiesUpdate,
|
||||
},
|
||||
|
||||
HelpSynopsis: strings.TrimSpace(sysHelp["policy"][0]),
|
||||
HelpDescription: strings.TrimSpace(sysHelp["policy"][1]),
|
||||
},
|
||||
|
||||
&framework.Path{
|
||||
Pattern: "capabilities",
|
||||
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"token": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Token of which capabilities are being requested",
|
||||
},
|
||||
"path": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Path for which token's capabilities are being fetched",
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.UpdateOperation: b.handleCapabilitiesUpdate,
|
||||
},
|
||||
|
||||
HelpSynopsis: strings.TrimSpace(sysHelp["policy"][0]),
|
||||
HelpDescription: strings.TrimSpace(sysHelp["policy"][1]),
|
||||
},
|
||||
|
||||
&framework.Path{
|
||||
Pattern: "audit-hash/(?P<path>.+)",
|
||||
|
||||
|
@ -381,6 +427,17 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) logical.Backend
|
|||
HelpSynopsis: strings.TrimSpace(sysHelp["rotate"][0]),
|
||||
HelpDescription: strings.TrimSpace(sysHelp["rotate"][1]),
|
||||
},
|
||||
|
||||
&framework.Path{
|
||||
Pattern: "rotate$",
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.UpdateOperation: b.handleRotate,
|
||||
},
|
||||
|
||||
HelpSynopsis: strings.TrimSpace(sysHelp["rotate"][0]),
|
||||
HelpDescription: strings.TrimSpace(sysHelp["rotate"][1]),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -853,6 +910,61 @@ func (b *SystemBackend) handlePolicyRead(
|
|||
}, nil
|
||||
}
|
||||
|
||||
// handleTokenCapabilitiesUpdate handles the "token-capabilities" endpoint to
|
||||
// fetch the capabilities of a token on a given path
|
||||
func (b *SystemBackend) handleCapabilitiesUpdate(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
log.Printf("\n\nvishal: logical_system: handleCapabilitiesUpdate: req:%#v data:%#v\n", req, data)
|
||||
tokenStore := b.Core.tokenStore
|
||||
token := data.Get("token").(string)
|
||||
if token == "" {
|
||||
token = req.ClientToken
|
||||
}
|
||||
if token == "" {
|
||||
return logical.ErrorResponse("missing token"), nil
|
||||
}
|
||||
path := data.Get("path").(string)
|
||||
if path == "" {
|
||||
return logical.ErrorResponse("missing path"), nil
|
||||
}
|
||||
log.Printf("vishal: received: clientToken:%s token:%s path:%s\n", req.ClientToken, token, path)
|
||||
te, err := tokenStore.Lookup(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if te == nil {
|
||||
return nil, errors.New("invalid token")
|
||||
}
|
||||
log.Printf("vishal: tokenEntry.Policies: %#v\n", te.Policies)
|
||||
if te.Policies == nil {
|
||||
return nil, nil
|
||||
}
|
||||
for _, tePolicy := range te.Policies {
|
||||
log.Printf("vishal: tePolicy:%s", tePolicy)
|
||||
if tePolicy == "root" {
|
||||
// Add all the capabilities
|
||||
}
|
||||
policy, err := b.Core.policyStore.GetPolicy(tePolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if policy == nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf("policy '%s' not found", tePolicy)), nil
|
||||
}
|
||||
if policy.Paths == nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf("policy '%s' does not contain any paths", tePolicy)), nil
|
||||
}
|
||||
for _, pathCapability := range policy.Paths {
|
||||
log.Printf("vishal: pathCapability: %#v\n", pathCapability)
|
||||
log.Printf("vishal: pathCapability.Prefix: %s\n", pathCapability.Prefix)
|
||||
log.Printf("vishal: pathCapability.Capabilities: %#v\n", pathCapability.Capabilities)
|
||||
if path == pathCapability.Prefix {
|
||||
log.Printf("vishal: found a match!!!!!!!!!!!\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// handlePolicySet handles the "policy/<name>" endpoint to set a policy
|
||||
func (b *SystemBackend) handlePolicySet(
|
||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package vault
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -253,6 +254,28 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error)
|
|||
HelpSynopsis: strings.TrimSpace(tokenRenewHelp),
|
||||
HelpDescription: strings.TrimSpace(tokenRenewHelp),
|
||||
},
|
||||
|
||||
&framework.Path{
|
||||
Pattern: "capabilities",
|
||||
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"token": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Token of which capabilities are being requested",
|
||||
},
|
||||
"path": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Path for which token's capabilities are being fetched",
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.UpdateOperation: t.handleCapabilitiesUpdate,
|
||||
},
|
||||
|
||||
HelpSynopsis: strings.TrimSpace(tokenCapabilitiesHelp),
|
||||
HelpDescription: strings.TrimSpace(tokenCapabilitiesHelp),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -530,6 +553,24 @@ func (ts *TokenStore) revokeTreeSalted(saltedId string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// handleCapabilitiesUpdate handles the auth/token/capabilities path for fetching
|
||||
// capabilities of a token on a given path
|
||||
func (ts *TokenStore) handleCapabilitiesUpdate(
|
||||
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
log.Printf("vishal: vault/token_store.go: handleCapabilitiesRead: req:%#v data:%#v\n", req, d)
|
||||
log.Println(d.Get("token").(string))
|
||||
te, err := ts.Lookup(d.Get("token").(string))
|
||||
if err != nil {
|
||||
log.Printf("vishal: token lookup err:%#v\n", err)
|
||||
}
|
||||
if te == nil {
|
||||
return logical.ErrorResponse("token does not exist"), nil
|
||||
}
|
||||
log.Printf("vishal: te.Policies:%#v\n", te.Policies)
|
||||
log.Println(d.Get("path").(string))
|
||||
return ts.handleCreateCommon(req, d, true)
|
||||
}
|
||||
|
||||
// handleCreate handles the auth/token/create path for creation of new orphan
|
||||
// tokens
|
||||
func (ts *TokenStore) handleCreateOrphan(
|
||||
|
@ -936,4 +977,5 @@ as revocation of tokens. The tokens are renewable if associated with a lease.`
|
|||
tokenRevokePrefixHelp = `This endpoint will delete all tokens generated under a prefix with their child tokens.`
|
||||
tokenRenewHelp = `This endpoint will renew the given token and prevent expiration.`
|
||||
tokenRenewSelfHelp = `This endpoint will renew the token used to call it and prevent expiration.`
|
||||
tokenCapabilitiesHelp = `This endpoint will return the capabilities of the given token on a given path.`
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue