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 }