Skip to content

Commit

Permalink
Stub adapters should allow the usage of an existing session
Browse files Browse the repository at this point in the history
StubAdapterBase is extended with an additional parameter and corresponding getter and setter - sessionId.
Class derivates can use this parameter to set their own ways for authentication.
Currently, pyVmomi uses mainly SoapStubAdapter() which uses a cookie to communicate the session id.
If a session id is provided on class initialization, the cookie will be set with that id.
connect.Connect(), connect.SmartConnect() and connect.SmartStubAdapter() are extended to allow the usage of an existing session.
When a session_id is provided the login step is skipped.

Usage example:
si1 = SmartConnect(host=vcip, user=vcAdmin, pwd=vcPassword, disableSslCertValidation=True)
soap_session_id = pyVim.connect.GetStub().GetSessionId()
si2 = SmartConnect(host=vcip, sessionId=soap_session_id, disableSslCertValidation=True)
  • Loading branch information
ddraganov committed Jan 29, 2024
1 parent 5ad215f commit 35f2743
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 10 deletions.
35 changes: 29 additions & 6 deletions pyVim/connect.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#############################################################
# Copyright (c) 2005-2023 VMware, Inc.
# Copyright (c) 2005-2024 VMware, Inc. All rights reserved.
#############################################################

# @file connect.py
Expand Down Expand Up @@ -216,6 +216,7 @@ def Connect(host='localhost',
tokenType=None,
disableSslCertValidation=False,
customHeaders=None,
sessionId=None,
# Deprecated
b64token=None,
# Deprecated
Expand Down Expand Up @@ -276,6 +277,9 @@ def Connect(host='localhost',
@param disableSslCertValidation: Creates an unverified SSL context when True.
@type customHeaders: dict
@param customHeaders: Dictionary with custom HTTP headers.
@type sessionId: string
@param sessionId: Allows usage of an existing session.
If supplied Login will not be attempted.
@param b64token: base64 encoded token
*** Deprecated: Use token instead ***
@type b64token: string
Expand Down Expand Up @@ -323,7 +327,8 @@ def Connect(host='localhost',
connectionPoolTimeout,
token=token,
tokenType=tokenType,
customHeaders=customHeaders)
customHeaders=customHeaders,
sessionId=sessionId)
SetSi(si)

return si
Expand Down Expand Up @@ -393,7 +398,8 @@ def __Login(host,
connectionPoolTimeout,
token,
tokenType,
customHeaders):
customHeaders,
sessionId):
"""
Private method that performs the actual Connect and returns a
connected service instance object.
Expand Down Expand Up @@ -439,6 +445,9 @@ def __Login(host,
@param tokenType: Select which type of Authentication and Authorization token to use.
@type customHeaders: dict
@param customHeaders: Dictionary with custom HTTP headers.
@type sessionId: string
@param sessionId: Allows usage of an existing session.
If supplied Login will not be attempted.
"""

# XXX remove the adapter and service arguments once dependent code is fixed
Expand Down Expand Up @@ -472,7 +481,8 @@ def __Login(host,
httpConnectionTimeout=httpConnectionTimeout,
connectionPoolTimeout=connectionPoolTimeout,
samlToken=samlToken,
customHeaders=customHeaders)
customHeaders=customHeaders,
sessionId=sessionId)

# Get Service instance
si = vim.ServiceInstance("ServiceInstance", stub)
Expand All @@ -494,6 +504,9 @@ def __Login(host,
else:
raise fault

if sessionId:
return si, stub

# Get a ticket if we're connecting to localhost and password is not specified
if host == 'localhost' and not pwd and not token:
try:
Expand Down Expand Up @@ -807,7 +820,8 @@ def SmartStubAdapter(host='localhost',
httpConnectionTimeout=None,
connectionPoolTimeout=CONNECTION_POOL_IDLE_TIMEOUT_SEC,
disableSslCertValidation=False,
customHeaders=None):
customHeaders=None,
sessionId=None):
"""
Determine the most preferred API version supported by the specified server,
then create a soap stub adapter using that version
Expand All @@ -825,6 +839,9 @@ def SmartStubAdapter(host='localhost',
@param disableSslCertValidation: Creates an unverified SSL context when True.
@type customHeaders: dict
@param customHeaders: Dictionary with custom HTTP headers.
@type sessionId: string
@param sessionId: Allows usage of an existing session.
If supplied Login will not be attempted.
"""
if preferredApiVersions is None:
preferredApiVersions = GetServiceVersions('vim25')
Expand Down Expand Up @@ -860,7 +877,8 @@ def SmartStubAdapter(host='localhost',
sslContext=sslContext,
httpConnectionTimeout=httpConnectionTimeout,
connectionPoolTimeout=connectionPoolTimeout,
customHeaders=customHeaders)
customHeaders=customHeaders,
sessionId=sessionId)


def SmartConnect(protocol='https',
Expand All @@ -883,6 +901,7 @@ def SmartConnect(protocol='https',
tokenType=None,
disableSslCertValidation=False,
customHeaders=None,
sessionId=None,
# Deprecated
b64token=None,
# Deprecated
Expand Down Expand Up @@ -944,6 +963,9 @@ def SmartConnect(protocol='https',
@param disableSslCertValidation: Creates an unverified SSL context when True.
@type customHeaders: dict
@param customHeaders: Dictionary with custom HTTP headers.
@type sessionId: string
@param sessionId: Allows usage of an existing session.
If supplied Login will not be attempted.
@param b64token: base64 encoded token
*** Deprecated: Use token instead ***
@type b64token: string
Expand Down Expand Up @@ -988,6 +1010,7 @@ def SmartConnect(protocol='https',
tokenType=tokenType,
disableSslCertValidation=disableSslCertValidation,
customHeaders=customHeaders,
sessionId=sessionId,
b64token=b64token,
mechanism=mechanism)

Expand Down
27 changes: 23 additions & 4 deletions pyVmomi/SoapAdapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from six import PY3
from six.moves import StringIO, zip
from six.moves.urllib.parse import urlparse
from six.moves.http_cookies import SimpleCookie

from . import Iso8601
from .StubAdapterAccessorImpl import StubAdapterAccessorMixin
Expand Down Expand Up @@ -95,6 +96,8 @@
WSSE_HEADER_START = "<{0} {1}>".format(WSSE_HEADER_TAG, WSSE_NS)
WSSE_HEADER_END = "</{0}>".format(WSSE_HEADER_TAG)

COOKIE_NAME = "vmware_soap_session"

# MethodFault type
MethodFault = GetVmodlType("vmodl.MethodFault")
# Localized MethodFault type
Expand Down Expand Up @@ -987,8 +990,10 @@ def EndElementHandler(self, tag):
# Method that must be provided by the implementation class:
# -- InvokeMethod(ManagedObject mo, Object methodInfo, Object[] args)
class StubAdapterBase(StubAdapterAccessorMixin):
def __init__(self, version):
def __init__(self, version, sessionId=None):
StubAdapterAccessorMixin.__init__(self)
self.sessionId = None
self.SetSessionId(sessionId)
self.ComputeVersionInfo(version)

# Compute the version information for the specified namespace
Expand All @@ -1008,6 +1013,12 @@ def ComputeVersionInfo(self, version):
self.versionId = ''
self.version = version

def GetSessionId(self):
return self.sessionId

def SetSessionId(self, sessionId):
self.sessionId = sessionId


# Base class that implements common functionality for SOAP-based stub adapters.
# Method that must be provided by the implementation class:
Expand Down Expand Up @@ -1384,6 +1395,7 @@ class SoapStubAdapter(SoapStubAdapterBase):
# sslContext = ssl.create_default_context(cafile=ca_cert_file)
# sslContext.load_cert_chain(key_file, cert_file)
# @param httpConnectionTimeout Timeout in secs for http requests.
# @param sessionId: Allows usage of an existing session
def __init__(self,
host='localhost',
port=443,
Expand All @@ -1406,15 +1418,16 @@ def __init__(self,
sslContext=None,
requestContext=None,
httpConnectionTimeout=None,
customHeaders=None):
customHeaders=None,
sessionId=None):
self._customHeaders = customHeaders
self.cookie = ""
if ns:
assert (version is None)
version = versionMap[ns]
elif not version:
version = 'vim.version.version9'
SoapStubAdapterBase.__init__(self, version=version)
self.cookie = ""
SoapStubAdapterBase.__init__(self, version=version, sessionId=sessionId)
if sock:
self.scheme = UnixSocketConnection
# Store sock in the host member variable because that's where
Expand Down Expand Up @@ -1549,6 +1562,8 @@ def InvokeMethod(self, mo, info, args, outerStub=None):

if cookie:
self.cookie = cookie
sessionId = SimpleCookie(cookie)[COOKIE_NAME].value
super(SoapStubAdapter, self).SetSessionId(sessionId)
if status == 200 or status == 500:
try:
fd = resp
Expand Down Expand Up @@ -1646,6 +1661,10 @@ def ReturnConnection(self, conn):
self.lock.release()
conn.close()

def SetSessionId(self, sessionId):
super(SoapStubAdapter, self).SetSessionId(sessionId)
self.cookie = '{0}="{1}"'.format(COOKIE_NAME, sessionId) if sessionId else ""

# Need to override the depcopy method. Since, the stub is not deep copyable
# due to the thread lock and connection pool, deep copy of a managed object
# fails. Further different instances of a managed object still share the
Expand Down

0 comments on commit 35f2743

Please sign in to comment.