mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			228 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package http
 | |
| 
 | |
| import (
 | |
| 	"io"
 | |
| 	"net"
 | |
| 	"net/http"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/hashicorp/vault/logical"
 | |
| 	"github.com/hashicorp/vault/vault"
 | |
| )
 | |
| 
 | |
| func handleLogical(core *vault.Core, dataOnly bool) http.Handler {
 | |
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | |
| 		// Determine the path...
 | |
| 		if !strings.HasPrefix(r.URL.Path, "/v1/") {
 | |
| 			respondError(w, http.StatusNotFound, nil)
 | |
| 			return
 | |
| 		}
 | |
| 		path := r.URL.Path[len("/v1/"):]
 | |
| 		if path == "" {
 | |
| 			respondError(w, http.StatusNotFound, nil)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		// Determine the operation
 | |
| 		var op logical.Operation
 | |
| 		switch r.Method {
 | |
| 		case "DELETE":
 | |
| 			op = logical.DeleteOperation
 | |
| 		case "GET":
 | |
| 			op = logical.ReadOperation
 | |
| 			// Need to call ParseForm to get query params loaded
 | |
| 			queryVals := r.URL.Query()
 | |
| 			listStr := queryVals.Get("list")
 | |
| 			if listStr != "" {
 | |
| 				list, err := strconv.ParseBool(listStr)
 | |
| 				if err != nil {
 | |
| 					respondError(w, http.StatusBadRequest, nil)
 | |
| 				}
 | |
| 				if list {
 | |
| 					op = logical.ListOperation
 | |
| 				}
 | |
| 			}
 | |
| 		case "POST", "PUT":
 | |
| 			op = logical.UpdateOperation
 | |
| 		case "LIST":
 | |
| 			op = logical.ListOperation
 | |
| 		default:
 | |
| 			respondError(w, http.StatusMethodNotAllowed, nil)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		// Parse the request if we can
 | |
| 		var req map[string]interface{}
 | |
| 		if op == logical.UpdateOperation {
 | |
| 			err := parseRequest(r, &req)
 | |
| 			if err == io.EOF {
 | |
| 				req = nil
 | |
| 				err = nil
 | |
| 			}
 | |
| 			if err != nil {
 | |
| 				respondError(w, http.StatusBadRequest, err)
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Make the internal request. We attach the connection info
 | |
| 		// as well in case this is an authentication request that requires
 | |
| 		// it. Vault core handles stripping this if we need to.
 | |
| 		resp, ok := request(core, w, r, requestAuth(r, &logical.Request{
 | |
| 			Operation:  op,
 | |
| 			Path:       path,
 | |
| 			Data:       req,
 | |
| 			Connection: getConnection(r),
 | |
| 		}))
 | |
| 		if !ok {
 | |
| 			return
 | |
| 		}
 | |
| 		if (op == logical.ReadOperation || op == logical.ListOperation) && resp == nil {
 | |
| 			respondError(w, http.StatusNotFound, nil)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		// Build the proper response
 | |
| 		respondLogical(w, r, path, dataOnly, resp)
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func respondLogical(w http.ResponseWriter, r *http.Request, path string, dataOnly bool, resp *logical.Response) {
 | |
| 	var httpResp interface{}
 | |
| 	if resp != nil {
 | |
| 		if resp.Redirect != "" {
 | |
| 			// If we have a redirect, redirect! We use a 307 code
 | |
| 			// because we don't actually know if its permanent.
 | |
| 			http.Redirect(w, r, resp.Redirect, 307)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		if dataOnly {
 | |
| 			respondOk(w, resp.Data)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		// Check if this is a raw response
 | |
| 		if _, ok := resp.Data[logical.HTTPContentType]; ok {
 | |
| 			respondRaw(w, r, path, resp)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		logicalResp := &LogicalResponse{
 | |
| 			Data:     resp.Data,
 | |
| 			Warnings: resp.Warnings(),
 | |
| 		}
 | |
| 		if resp.Secret != nil {
 | |
| 			logicalResp.LeaseID = resp.Secret.LeaseID
 | |
| 			logicalResp.Renewable = resp.Secret.Renewable
 | |
| 			logicalResp.LeaseDuration = int(resp.Secret.TTL.Seconds())
 | |
| 		}
 | |
| 
 | |
| 		// If we have authentication information, then
 | |
| 		// set up the result structure.
 | |
| 		if resp.Auth != nil {
 | |
| 			logicalResp.Auth = &Auth{
 | |
| 				ClientToken:   resp.Auth.ClientToken,
 | |
| 				AccessorID:    resp.Auth.AccessorID,
 | |
| 				Policies:      resp.Auth.Policies,
 | |
| 				Metadata:      resp.Auth.Metadata,
 | |
| 				LeaseDuration: int(resp.Auth.TTL.Seconds()),
 | |
| 				Renewable:     resp.Auth.Renewable,
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		httpResp = logicalResp
 | |
| 	}
 | |
| 
 | |
| 	// Respond
 | |
| 	respondOk(w, httpResp)
 | |
| }
 | |
| 
 | |
| // respondRaw is used when the response is using HTTPContentType and HTTPRawBody
 | |
| // to change the default response handling. This is only used for specific things like
 | |
| // returning the CRL information on the PKI backends.
 | |
| func respondRaw(w http.ResponseWriter, r *http.Request, path string, resp *logical.Response) {
 | |
| 	// Ensure this is never a secret or auth response
 | |
| 	if resp.Secret != nil || resp.Auth != nil {
 | |
| 		respondError(w, http.StatusInternalServerError, nil)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Get the status code
 | |
| 	statusRaw, ok := resp.Data[logical.HTTPStatusCode]
 | |
| 	if !ok {
 | |
| 		respondError(w, http.StatusInternalServerError, nil)
 | |
| 		return
 | |
| 	}
 | |
| 	status, ok := statusRaw.(int)
 | |
| 	if !ok {
 | |
| 		respondError(w, http.StatusInternalServerError, nil)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Get the header
 | |
| 	contentTypeRaw, ok := resp.Data[logical.HTTPContentType]
 | |
| 	if !ok {
 | |
| 		respondError(w, http.StatusInternalServerError, nil)
 | |
| 		return
 | |
| 	}
 | |
| 	contentType, ok := contentTypeRaw.(string)
 | |
| 	if !ok {
 | |
| 		respondError(w, http.StatusInternalServerError, nil)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Get the body
 | |
| 	bodyRaw, ok := resp.Data[logical.HTTPRawBody]
 | |
| 	if !ok {
 | |
| 		respondError(w, http.StatusInternalServerError, nil)
 | |
| 		return
 | |
| 	}
 | |
| 	body, ok := bodyRaw.([]byte)
 | |
| 	if !ok {
 | |
| 		respondError(w, http.StatusInternalServerError, nil)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Write the response
 | |
| 	w.Header().Set("Content-Type", contentType)
 | |
| 	w.WriteHeader(status)
 | |
| 	w.Write(body)
 | |
| }
 | |
| 
 | |
| // getConnection is used to format the connection information for
 | |
| // attaching to a logical request
 | |
| func getConnection(r *http.Request) (connection *logical.Connection) {
 | |
| 	var remoteAddr string
 | |
| 
 | |
| 	remoteAddr, _, err := net.SplitHostPort(r.RemoteAddr)
 | |
| 	if err != nil {
 | |
| 		remoteAddr = ""
 | |
| 	}
 | |
| 
 | |
| 	connection = &logical.Connection{
 | |
| 		RemoteAddr: remoteAddr,
 | |
| 		ConnState:  r.TLS,
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| type LogicalResponse struct {
 | |
| 	LeaseID       string                 `json:"lease_id"`
 | |
| 	Renewable     bool                   `json:"renewable"`
 | |
| 	LeaseDuration int                    `json:"lease_duration"`
 | |
| 	Data          map[string]interface{} `json:"data"`
 | |
| 	Warnings      []string               `json:"warnings"`
 | |
| 	Auth          *Auth                  `json:"auth"`
 | |
| }
 | |
| 
 | |
| type Auth struct {
 | |
| 	ClientToken   string            `json:"client_token"`
 | |
| 	AccessorID    string            `json:"accessor_id"`
 | |
| 	Policies      []string          `json:"policies"`
 | |
| 	Metadata      map[string]string `json:"metadata"`
 | |
| 	LeaseDuration int               `json:"lease_duration"`
 | |
| 	Renewable     bool              `json:"renewable"`
 | |
| }
 | 
