github.com/vmware/govmomi@v0.37.2/session/manager.go (about)

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