github.com/vmware/govmomi@v0.51.0/session/manager.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 session 6 7 import ( 8 "context" 9 "net/url" 10 "os" 11 "strings" 12 "sync" 13 14 "github.com/vmware/govmomi/fault" 15 "github.com/vmware/govmomi/property" 16 "github.com/vmware/govmomi/vim25" 17 "github.com/vmware/govmomi/vim25/methods" 18 "github.com/vmware/govmomi/vim25/mo" 19 "github.com/vmware/govmomi/vim25/types" 20 ) 21 22 // Locale defaults to "en_US" and can be overridden via this var or the GOVMOMI_LOCALE env var. 23 // A value of "_" uses the server locale setting. 24 var Locale = os.Getenv("GOVMOMI_LOCALE") 25 26 func init() { 27 if Locale == "_" { 28 Locale = "" 29 } else if Locale == "" { 30 Locale = "en_US" 31 } 32 } 33 34 // Secret returns the contents if a file path value is given, otherwise returns value itself. 35 func Secret(value string) (string, error) { 36 if len(value) == 0 { 37 return value, nil 38 } 39 contents, err := os.ReadFile(value) 40 if err != nil { 41 if os.IsPermission(err) { 42 return "", err 43 } 44 return value, nil 45 } 46 return strings.TrimSpace(string(contents)), nil 47 } 48 49 type Manager struct { 50 client *vim25.Client 51 userSession *types.UserSession 52 mu sync.Mutex 53 } 54 55 func NewManager(client *vim25.Client) *Manager { 56 m := Manager{ 57 client: client, 58 } 59 60 return &m 61 } 62 63 func (sm *Manager) Reference() types.ManagedObjectReference { 64 return *sm.client.ServiceContent.SessionManager 65 } 66 67 func (sm *Manager) SetLocale(ctx context.Context, locale string) error { 68 req := types.SetLocale{ 69 This: sm.Reference(), 70 Locale: locale, 71 } 72 73 _, err := methods.SetLocale(ctx, sm.client, &req) 74 return err 75 } 76 77 func (sm *Manager) setUserSession(val *types.UserSession) { 78 sm.mu.Lock() 79 sm.userSession = val 80 sm.mu.Unlock() 81 } 82 83 func (sm *Manager) getUserSession() (types.UserSession, bool) { 84 sm.mu.Lock() 85 defer sm.mu.Unlock() 86 if sm.userSession == nil { 87 return types.UserSession{}, false 88 } 89 return *sm.userSession, true 90 } 91 92 func (sm *Manager) Login(ctx context.Context, u *url.Userinfo) error { 93 req := types.Login{ 94 This: sm.Reference(), 95 Locale: Locale, 96 } 97 98 if u != nil { 99 req.UserName = u.Username() 100 if pw, ok := u.Password(); ok { 101 req.Password = pw 102 } 103 } 104 105 login, err := methods.Login(ctx, sm.client, &req) 106 if err != nil { 107 return err 108 } 109 110 sm.setUserSession(&login.Returnval) 111 return nil 112 } 113 114 // LoginExtensionByCertificate uses the vCenter SDK tunnel to login using a client certificate. 115 // The client certificate can be set using the soap.Client.SetCertificate method. 116 func (sm *Manager) LoginExtensionByCertificate(ctx context.Context, key string) error { 117 c := sm.client 118 u := c.URL() 119 if u.Hostname() != "sdkTunnel" { 120 sc := c.Tunnel() 121 c = &vim25.Client{ 122 Client: sc, 123 RoundTripper: sc, 124 ServiceContent: c.ServiceContent, 125 } 126 // When http.Transport.Proxy is used, our thumbprint checker is bypassed, resulting in: 127 // "Post https://sdkTunnel:8089/sdk: x509: certificate is valid for $vcenter_hostname, not sdkTunnel" 128 // The only easy way around this is to disable verification for the call to LoginExtensionByCertificate(). 129 // TODO: find a way to avoid disabling InsecureSkipVerify. 130 c.DefaultTransport().TLSClientConfig.InsecureSkipVerify = true 131 } 132 133 req := types.LoginExtensionByCertificate{ 134 This: sm.Reference(), 135 ExtensionKey: key, 136 Locale: Locale, 137 } 138 139 login, err := methods.LoginExtensionByCertificate(ctx, c, &req) 140 if err != nil { 141 return err 142 } 143 144 // Copy the session cookie 145 sm.client.Jar.SetCookies(u, c.Jar.Cookies(c.URL())) 146 147 sm.setUserSession(&login.Returnval) 148 return nil 149 } 150 151 func (sm *Manager) LoginByToken(ctx context.Context) error { 152 req := types.LoginByToken{ 153 This: sm.Reference(), 154 Locale: Locale, 155 } 156 157 login, err := methods.LoginByToken(ctx, sm.client, &req) 158 if err != nil { 159 return err 160 } 161 162 sm.setUserSession(&login.Returnval) 163 return nil 164 } 165 166 func (sm *Manager) Logout(ctx context.Context) error { 167 req := types.Logout{ 168 This: sm.Reference(), 169 } 170 171 _, err := methods.Logout(ctx, sm.client, &req) 172 if err != nil { 173 return err 174 } 175 176 sm.setUserSession(nil) 177 return nil 178 } 179 180 // UserSession retrieves and returns the SessionManager's CurrentSession field. 181 // Nil is returned if the session is not authenticated. 182 func (sm *Manager) UserSession(ctx context.Context) (*types.UserSession, error) { 183 var mgr mo.SessionManager 184 185 pc := property.DefaultCollector(sm.client) 186 err := pc.RetrieveOne(ctx, sm.Reference(), []string{"currentSession"}, &mgr) 187 if err != nil { 188 // It's OK if we can't retrieve properties because we're not authenticated 189 if fault.Is(err, &types.NotAuthenticated{}) { 190 return nil, nil 191 } 192 193 return nil, err 194 } 195 196 return mgr.CurrentSession, nil 197 } 198 199 func (sm *Manager) TerminateSession(ctx context.Context, sessionId []string) error { 200 req := types.TerminateSession{ 201 This: sm.Reference(), 202 SessionId: sessionId, 203 } 204 205 _, err := methods.TerminateSession(ctx, sm.client, &req) 206 return err 207 } 208 209 // SessionIsActive checks whether the session that was created at login is 210 // still valid. This function only works against vCenter. 211 func (sm *Manager) SessionIsActive(ctx context.Context) (bool, error) { 212 userSession, ok := sm.getUserSession() 213 if !ok { 214 return false, nil 215 } 216 217 req := types.SessionIsActive{ 218 This: sm.Reference(), 219 SessionID: userSession.Key, 220 UserName: userSession.UserName, 221 } 222 223 active, err := methods.SessionIsActive(ctx, sm.client, &req) 224 if err != nil { 225 return false, err 226 } 227 228 return active.Returnval, err 229 } 230 231 func (sm *Manager) AcquireGenericServiceTicket(ctx context.Context, spec types.BaseSessionManagerServiceRequestSpec) (*types.SessionManagerGenericServiceTicket, error) { 232 req := types.AcquireGenericServiceTicket{ 233 This: sm.Reference(), 234 Spec: spec, 235 } 236 237 res, err := methods.AcquireGenericServiceTicket(ctx, sm.client, &req) 238 if err != nil { 239 return nil, err 240 } 241 242 return &res.Returnval, nil 243 } 244 245 func (sm *Manager) AcquireLocalTicket(ctx context.Context, userName string) (*types.SessionManagerLocalTicket, error) { 246 req := types.AcquireLocalTicket{ 247 This: sm.Reference(), 248 UserName: userName, 249 } 250 251 res, err := methods.AcquireLocalTicket(ctx, sm.client, &req) 252 if err != nil { 253 return nil, err 254 } 255 256 return &res.Returnval, nil 257 } 258 259 func (sm *Manager) AcquireCloneTicket(ctx context.Context) (string, error) { 260 req := types.AcquireCloneTicket{ 261 This: sm.Reference(), 262 } 263 264 res, err := methods.AcquireCloneTicket(ctx, sm.client, &req) 265 if err != nil { 266 return "", err 267 } 268 269 return res.Returnval, nil 270 } 271 272 func (sm *Manager) CloneSession(ctx context.Context, ticket string) error { 273 req := types.CloneSession{ 274 This: sm.Reference(), 275 CloneTicket: ticket, 276 } 277 278 res, err := methods.CloneSession(ctx, sm.client, &req) 279 if err != nil { 280 return err 281 } 282 283 sm.setUserSession(&res.Returnval) 284 return nil 285 } 286 287 func (sm *Manager) UpdateServiceMessage(ctx context.Context, message string) error { 288 req := types.UpdateServiceMessage{ 289 This: sm.Reference(), 290 Message: message, 291 } 292 293 _, err := methods.UpdateServiceMessage(ctx, sm.client, &req) 294 295 return err 296 } 297 298 func (sm *Manager) ImpersonateUser(ctx context.Context, name string) error { 299 req := types.ImpersonateUser{ 300 This: sm.Reference(), 301 UserName: name, 302 Locale: Locale, 303 } 304 305 res, err := methods.ImpersonateUser(ctx, sm.client, &req) 306 if err != nil { 307 return err 308 } 309 310 sm.setUserSession(&res.Returnval) 311 return nil 312 }