3131import urllib
3232import io
3333import sys
34+ import Cookie
3435
3536from datetime import datetime
3637from functools import wraps
@@ -67,6 +68,45 @@ def new_f(*args, **kwargs):
6768 return new_f
6869
6970
71+ def _parse_cookies (cookie_str , dictionary ):
72+ """Tries to parse any key-value pairs of cookies in a string,
73+ then updates the the dictionary with any key-value pairs found.
74+
75+ **Example**::
76+ dictionary = {}
77+ _parse_cookies('my=value', dictionary)
78+ # Now the following is True
79+ dictionary['my'] == 'value'
80+
81+ :param cookie_str: A string containing "key=value" pairs from an HTTP "Set-Cookie" header.
82+ :type cookie_str: ``str``
83+ :param dictionary: A dictionary to update with any found key-value pairs.
84+ :type dictionary: ``dict``
85+ """
86+ parsed_cookie = Cookie .SimpleCookie (cookie_str )
87+ for cookie in parsed_cookie .values ():
88+ dictionary [cookie .key ] = cookie .coded_value
89+
90+
91+ def _make_cookie_header (cookies ):
92+ """
93+ Takes a list of 2-tuples of key-value pairs of
94+ cookies, and returns a valid HTTP ``Cookie``
95+ header.
96+
97+ **Example**::
98+
99+ header = _make_cookie_header([("key", "value"), ("key_2", "value_2")])
100+ # Now the following is True
101+ header == "key=value; key_2=value_2"
102+
103+ :param cookies: A list of 2-tuples of cookie key-value pairs.
104+ :type cookies: ``list`` of 2-tuples
105+ :return: ``str` An HTTP header cookie string.
106+ :rtype: ``str``
107+ """
108+ return "; " .join ("%s=%s" % (key , value ) for key , value in cookies )
109+
70110# Singleton values to eschew None
71111class _NoAuthenticationToken (object ):
72112 """The value stored in a :class:`Context` or :class:`splunklib.client.Service`
@@ -225,7 +265,7 @@ def f():
225265 @wraps (request_fun )
226266 def wrapper (self , * args , ** kwargs ):
227267 if self .token is _NoAuthenticationToken and \
228- self .cookie is _NoAuthenticationToken :
268+ not self .has_cookies () :
229269 # Not yet logged in.
230270 if self .autologin and self .username and self .password :
231271 # This will throw an uncaught
@@ -428,9 +468,27 @@ def __init__(self, handler=None, **kwargs):
428468 self .username = kwargs .get ("username" , "" )
429469 self .password = kwargs .get ("password" , "" )
430470 self .autologin = kwargs .get ("autologin" , False )
431- self .cookie = kwargs .get ("cookie" , _NoAuthenticationToken )
432- if self .cookie is None : # In case someone explicitly passes cookie=None
433- self .cookie = _NoAuthenticationToken
471+
472+ # Store any cookies in the self.http._cookies dict
473+ if kwargs .has_key ("cookie" ) and kwargs ['cookie' ] not in [None , _NoAuthenticationToken ]:
474+ _parse_cookies (kwargs ["cookie" ], self .http ._cookies )
475+
476+ def get_cookies (self ):
477+ """Gets the dictionary of cookies from the ``HttpLib`` member of this instance.
478+
479+ :return: Dictionary of cookies stored on the ``self.http``.
480+ :rtype: ``dict``
481+ """
482+ return self .http ._cookies
483+
484+ def has_cookies (self ):
485+ """Returns true if the ``HttpLib` member of this instance has at least
486+ one cookie stored.
487+
488+ :return: ``True`` if there is at least one cookie, else ``False``
489+ :rtype: ``bool``
490+ """
491+ return len (self .get_cookies ()) > 0
434492
435493 # Shared per-context request headers
436494 @property
@@ -443,8 +501,8 @@ def _auth_headers(self):
443501
444502 :returns: A list of 2-tuples containing key and value
445503 """
446- if self .cookie is not _NoAuthenticationToken :
447- return [("cookie " , self .cookie )]
504+ if self .has_cookies () :
505+ return [("Cookie " , _make_cookie_header ( self .get_cookies (). items ()) )]
448506 elif self .token is _NoAuthenticationToken :
449507 return []
450508 else :
@@ -761,10 +819,10 @@ def login(self):
761819 c = binding.Context(...).login()
762820 # Then issue requests...
763821 """
764- # If self.cookie and self.token only, use the cookie
765- if self .cookie is not _NoAuthenticationToken and \
822+
823+ if self .has_cookies () and \
766824 (not self .username and not self .password ):
767- # If we were passed a session cookie, but no username or
825+ # If we were passed session cookie(s) , but no username or
768826 # password, then login is a nop, since we're automatically
769827 # logged in.
770828 return
@@ -784,12 +842,6 @@ def login(self):
784842 password = self .password ,
785843 cookie = "1" ) # In Splunk 6.2+, passing "cookie=1" will return the "set-cookie" header
786844
787- # Store the cookie
788- for key , value in response .headers :
789- if key .lower () == "set-cookie" :
790- self .cookie = value
791- break
792-
793845 body = response .body .read ()
794846 session = XML (body ).findtext ("./sessionKey" )
795847 self .token = "Splunk %s" % session
@@ -801,8 +853,9 @@ def login(self):
801853 raise
802854
803855 def logout (self ):
804- """Forgets the current session token."""
856+ """Forgets the current session token, and cookies ."""
805857 self .token = _NoAuthenticationToken
858+ self .http ._cookies = {}
806859 return self
807860
808861 def _abspath (self , path_segment ,
@@ -889,6 +942,9 @@ def connect(**kwargs):
889942 :param token: The current session token (optional). Session tokens can be
890943 shared across multiple service instances.
891944 :type token: ``string``
945+ :param cookie: A session cookie. When provided, you don't need to call :meth:`login`.
946+ This parameter is only supported for Splunk 6.2+.
947+ :type cookie: ``string``
892948 :param username: The Splunk account username, which is used to
893949 authenticate the Splunk instance.
894950 :type username: ``string``
@@ -1030,6 +1086,7 @@ class HttpLib(object):
10301086 """
10311087 def __init__ (self , custom_handler = None ):
10321088 self .handler = handler () if custom_handler is None else custom_handler
1089+ self ._cookies = {}
10331090
10341091 def delete (self , url , headers = None , ** kwargs ):
10351092 """Sends a DELETE request to a URL.
@@ -1141,10 +1198,16 @@ def request(self, url, message, **kwargs):
11411198 raise HTTPError (response )
11421199
11431200 # Update the cookie with any HTTP request
1144- for key , value in response .headers :
1201+ # Initially, assume list of 2-tuples
1202+ key_value_tuples = response .headers
1203+ # If response.headers is a dict, get the key-value pairs as 2-tuples
1204+ # this is the case when using urllib2
1205+ if isinstance (response .headers , dict ):
1206+ key_value_tuples = response .headers .items ()
1207+ for key , value in key_value_tuples :
11451208 if key .lower () == "set-cookie" :
1146- self .cookie = value
1147- break
1209+ _parse_cookies ( value , self ._cookies )
1210+
11481211 return response
11491212
11501213
@@ -1256,7 +1319,6 @@ def request(url, message, **kwargs):
12561319 "Host" : host ,
12571320 "User-Agent" : "splunk-sdk-python/0.1" ,
12581321 "Accept" : "*/*" ,
1259- "Cookie" : "1"
12601322 } # defaults
12611323 for key , value in message ["headers" ]:
12621324 head [key ] = value
0 commit comments