github.com/vmware/govmomi@v0.51.0/vapi/crypto/simulator/simulator.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package simulator
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"net/http"
    11  	"net/url"
    12  	"path"
    13  	"time"
    14  
    15  	"github.com/google/uuid"
    16  
    17  	"github.com/vmware/govmomi/simulator"
    18  	"github.com/vmware/govmomi/vapi/crypto"
    19  	"github.com/vmware/govmomi/vapi/crypto/internal"
    20  	vapi "github.com/vmware/govmomi/vapi/simulator"
    21  	"github.com/vmware/govmomi/vim25/types"
    22  )
    23  
    24  const (
    25  	typeNativeProvider = string(types.KmipClusterInfoKmsManagementTypeNativeProvider)
    26  	backupPath         = "/cryptomanager/kms/"
    27  )
    28  
    29  func init() {
    30  	simulator.RegisterEndpoint(func(s *simulator.Service, r *simulator.Registry) {
    31  		New(r, s.Listen).Register(s, r)
    32  	})
    33  }
    34  
    35  // Handler implements the Cluster Modules API simulator
    36  type Handler struct {
    37  	URL *url.URL
    38  	Map *simulator.Registry
    39  }
    40  
    41  // New creates a Handler instance
    42  func New(r *simulator.Registry, u *url.URL) *Handler {
    43  	return &Handler{
    44  		Map: r,
    45  		URL: u,
    46  	}
    47  }
    48  
    49  // Register Namespace Management API paths with the vapi simulator's http.ServeMux
    50  func (h *Handler) Register(s *simulator.Service, r *simulator.Registry) {
    51  	if r.IsVPX() {
    52  		s.HandleFunc(internal.KmsProvidersPath, h.providers)
    53  		s.HandleFunc(internal.KmsProvidersPath+"/", h.providersID)
    54  		s.HandleFunc(backupPath, h.backup)
    55  	}
    56  }
    57  
    58  // We need to use the simulator objects directly when updating fields (e.g. HasBackup)
    59  // Skipping the trouble of locking for now, as existing use-cases would not race.
    60  func (h *Handler) find(id string) *types.KmipClusterInfo {
    61  	m := h.Map.CryptoManager()
    62  	for i := range m.KmipServers {
    63  		p := &m.KmipServers[i]
    64  		if p.ClusterId.Id == id {
    65  			return p
    66  		}
    67  	}
    68  	return nil
    69  }
    70  
    71  func (h *Handler) backup(w http.ResponseWriter, r *http.Request) {
    72  	id := path.Base(r.RequestURI)
    73  	p := h.find(id)
    74  	if p == nil {
    75  		vapi.ApiErrorNotFound(w)
    76  		return
    77  	}
    78  
    79  	// Content of the simulated backup does not matter for the use-case we're covering:
    80  	// Export sets HasBackup=true, which sets CryptoManagerKmipClusterStatus.OverallStatus=green
    81  	p.HasBackup = types.NewBool(true)
    82  
    83  	name := fmt.Sprintf("%s%s.p12", id, time.Now().Format(time.RFC3339))
    84  
    85  	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name))
    86  	_ = json.NewEncoder(w).Encode(p)
    87  }
    88  
    89  func (h *Handler) providers(w http.ResponseWriter, r *http.Request) {
    90  	switch r.Method {
    91  	case http.MethodPost:
    92  		switch r.URL.Query().Get("action") {
    93  		case "":
    94  			var spec crypto.KmsProviderCreateSpec
    95  
    96  			if vapi.Decode(r, w, &spec) {
    97  				if h.find(spec.Provider) != nil {
    98  					vapi.ApiErrorAlreadyExists(w)
    99  					return
   100  				}
   101  			}
   102  
   103  			m := h.Map.CryptoManager()
   104  			m.KmipServers = append(m.KmipServers, types.KmipClusterInfo{
   105  				ClusterId:      types.KeyProviderId{Id: spec.Provider},
   106  				ManagementType: typeNativeProvider,
   107  				TpmRequired:    &spec.Constraints.TpmRequired,
   108  				HasBackup:      types.NewBool(false),
   109  			})
   110  		case "export":
   111  			var spec crypto.KmsProviderExportSpec
   112  			var p *types.KmipClusterInfo
   113  
   114  			if vapi.Decode(r, w, &spec) {
   115  				if p = h.find(spec.Provider); p == nil {
   116  					vapi.ApiErrorNotFound(w)
   117  					return
   118  				}
   119  				if p.ManagementType != typeNativeProvider {
   120  					vapi.ApiErrorUnsupported(w)
   121  					return
   122  				}
   123  
   124  				u := url.URL{
   125  					Scheme: h.URL.Scheme,
   126  					Host:   h.URL.Host,
   127  					Path:   backupPath + spec.Provider,
   128  				}
   129  
   130  				res := crypto.KmsProviderExport{
   131  					Type: "LOCATION",
   132  					Location: &crypto.KmsProviderExportLocation{
   133  						URL: u.String(),
   134  						DownloadToken: crypto.KmsProviderDownloadToken{
   135  							Token:  uuid.NewString(),
   136  							Expiry: time.Now().Add(time.Minute).Format(time.RFC3339),
   137  						},
   138  					},
   139  				}
   140  
   141  				vapi.StatusOK(w, res)
   142  			}
   143  		}
   144  	default:
   145  		w.WriteHeader(http.StatusMethodNotAllowed)
   146  	}
   147  }
   148  
   149  func (h *Handler) providersID(w http.ResponseWriter, r *http.Request) {
   150  	switch r.Method {
   151  	case http.MethodDelete:
   152  		id := path.Base(r.RequestURI)
   153  		p := h.find(id)
   154  		if p == nil {
   155  			vapi.ApiErrorNotFound(w)
   156  			return
   157  		}
   158  		if p.ManagementType != typeNativeProvider {
   159  			vapi.ApiErrorUnsupported(w)
   160  			return
   161  		}
   162  		m := h.Map.CryptoManager()
   163  		ctx := &simulator.Context{Map: h.Map}
   164  		_ = m.UnregisterKmsCluster(ctx, &types.UnregisterKmsCluster{
   165  			This:      m.Self,
   166  			ClusterId: types.KeyProviderId{Id: id},
   167  		})
   168  		vapi.StatusOK(w)
   169  	default:
   170  		w.WriteHeader(http.StatusMethodNotAllowed)
   171  	}
   172  }