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  }