55
66from requests .auth import AuthBase
77from requests .models import Response
8- from requests .compat import urlparse , StringIO
8+ from requests .compat import urlparse
99from requests .structures import CaseInsensitiveDict
1010from requests .cookies import cookiejar_from_dict
1111
2828OPTIONAL = 2
2929DISABLED = 3
3030
31+
3132class SanitizedResponse (Response ):
3233 """The :class:`Response <Response>` object, which contains a server's
3334 response to an HTTP request.
@@ -78,12 +79,10 @@ def _negotiate_value(response):
7879
7980
8081class HTTPKerberosAuth (AuthBase ):
81- """Attaches HTTP GSSAPI/Kerberos Authentication to the given Request
82- object."""
83- def __init__ (
84- self , mutual_authentication = REQUIRED ,
85- service = "HTTP" , delegate = False , force_preemptive = False ,
86- principal = None , hostname_override = None , sanitize_mutual_error_response = True ):
82+ """Attaches HTTP GSSAPI Authentication to the given Request object."""
83+ def __init__ (self , mutual_authentication = REQUIRED , service = "HTTP" ,
84+ delegate = False , force_preemptive = False , principal = None ,
85+ hostname_override = None , sanitize_mutual_error_response = True ):
8786 self .context = {}
8887 self .mutual_authentication = mutual_authentication
8988 self .delegate = delegate
@@ -110,10 +109,13 @@ def generate_request_header(self, response, host, is_preemptive=False):
110109
111110 try :
112111 # contexts still need to be stored by host, but hostname_override
113- # allows use of an arbitrary hostname for the kerberos exchange
112+ # allows use of an arbitrary hostname for the GSSAPI exchange
114113 # (eg, in cases of aliased hosts, internal vs external, CNAMEs
115114 # w/ name-based HTTP hosting)
116- kerb_host = self .hostname_override if self .hostname_override is not None else host
115+ kerb_host = host
116+ if self .hostname_override :
117+ kerb_host = self .hostname_override
118+
117119 kerb_spn = "{0}@{1}" .format (self .service , kerb_host )
118120
119121 creds = None
@@ -144,7 +146,7 @@ def generate_request_header(self, response, host, is_preemptive=False):
144146 raise KerberosExchangeError ("%s failed: %s" % (gss_stage , msg ))
145147
146148 def authenticate_user (self , response , ** kwargs ):
147- """Handles user authentication with gssapi/kerberos """
149+ """Handles user authentication with GSSAPI """
148150
149151 host = urlparse (response .url ).hostname
150152
@@ -170,15 +172,15 @@ def authenticate_user(self, response, **kwargs):
170172 return _r
171173
172174 def handle_401 (self , response , ** kwargs ):
173- """Handles 401's, attempts to use gssapi/kerberos authentication"""
175+ """Handles 401's, attempts to use GSSAPI authentication"""
174176
175177 log .debug ("handle_401(): Handling: 401" )
176178 if _negotiate_value (response ) is not None :
177179 _r = self .authenticate_user (response , ** kwargs )
178180 log .debug ("handle_401(): returning {0}" .format (_r ))
179181 return _r
180182 else :
181- log .debug ("handle_401(): Kerberos is not supported" )
183+ log .debug ("handle_401(): GSSAPI is not supported" )
182184 log .debug ("handle_401(): returning {0}" .format (response ))
183185 return response
184186
@@ -189,44 +191,42 @@ def handle_other(self, response):
189191
190192 log .debug ("handle_other(): Handling: %d" % response .status_code )
191193
192- if self .mutual_authentication in (REQUIRED , OPTIONAL ):
193-
194- is_http_error = response .status_code >= 400
195-
196- if _negotiate_value (response ) is not None :
197- log .debug ("handle_other(): Authenticating the server" )
198- if not self .authenticate_server (response ):
199- # Mutual authentication failure when mutual auth is wanted,
200- # raise an exception so the user doesn't use an untrusted
201- # response.
202- log .error ("handle_other(): Mutual authentication failed" )
203- raise MutualAuthenticationError ("Unable to authenticate "
204- "{0}" .format (response ))
205-
206- # Authentication successful
207- log .debug ("handle_other(): returning {0}" .format (response ))
208- return response
209-
210- elif is_http_error or self .mutual_authentication == OPTIONAL :
211- if not response .ok :
212- log .error ("handle_other(): Mutual authentication unavailable "
213- "on {0} response" .format (response .status_code ))
214-
215- if (self .mutual_authentication == REQUIRED and
216- self .sanitize_mutual_error_response ):
217- return SanitizedResponse (response )
218- else :
219- return response
220- else :
221- # Unable to attempt mutual authentication when mutual auth is
222- # required, raise an exception so the user doesn't use an
223- # untrusted response.
194+ if self .mutual_authentication not in (REQUIRED , OPTIONAL ):
195+ log .debug ("handle_other(): returning {0}" .format (response ))
196+ return response
197+
198+ is_http_error = response .status_code >= 400
199+
200+ if _negotiate_value (response ) is not None :
201+ log .debug ("handle_other(): Authenticating the server" )
202+ if not self .authenticate_server (response ):
203+ # Mutual authentication failure when mutual auth is wanted,
204+ # raise an exception so the user doesn't use an untrusted
205+ # response.
224206 log .error ("handle_other(): Mutual authentication failed" )
225- raise MutualAuthenticationError ("Unable to authenticate "
226- "{0}" .format (response ))
227- else :
207+ raise MutualAuthenticationError (
208+ "Unable to authenticate {0}" .format (response ))
209+
210+ # Authentication successful
228211 log .debug ("handle_other(): returning {0}" .format (response ))
229212 return response
213+ elif is_http_error or self .mutual_authentication == OPTIONAL :
214+ if not response .ok :
215+ log .error (
216+ "handle_other(): Mutual authentication unavailable on"
217+ " {0} response" .format (response .status_code ))
218+
219+ if self .mutual_authentication == REQUIRED and \
220+ self .sanitize_mutual_error_response :
221+ return SanitizedResponse (response )
222+ return response
223+ else :
224+ # Unable to attempt mutual authentication when mutual auth is
225+ # required, raise an exception so the user doesn't use an
226+ # untrusted response.
227+ log .error ("handle_other(): Mutual authentication failed" )
228+ raise MutualAuthenticationError (
229+ "Unable to authenticate {0}" .format (response ))
230230
231231 def authenticate_server (self , response ):
232232 """
@@ -241,7 +241,8 @@ def authenticate_server(self, response):
241241 host = urlparse (response .url ).hostname
242242
243243 try :
244- result = self .context [host ].step (_negotiate_value (response ))
244+ # If the handshake isn't complete here, nothing we can do
245+ self .context [host ].step (_negotiate_value (response ))
245246 except gssapi .exceptions .GSSError as error :
246247 log .exception ("authenticate_server(): context stepping failed:" )
247248 log .exception (error .gen_message ())
@@ -251,7 +252,7 @@ def authenticate_server(self, response):
251252 return True
252253
253254 def handle_response (self , response , ** kwargs ):
254- """Takes the given response and tries kerberos- auth, as needed."""
255+ """Takes the given response and tries GSSAPI auth, as needed."""
255256 num_401s = kwargs .pop ('num_401s' , 0 )
256257
257258 if self .pos is not None :
@@ -272,10 +273,10 @@ def handle_response(self, response, **kwargs):
272273 # Authentication has failed. Return the 401 response.
273274 log .debug ("handle_response(): returning 401 %s" , response )
274275 return response
275- else :
276- _r = self .handle_other (response )
277- log .debug ("handle_response(): returning %s" , _r )
278- return _r
276+
277+ _r = self .handle_other (response )
278+ log .debug ("handle_response(): returning %s" , _r )
279+ return _r
279280
280281 def deregister (self , response ):
281282 """Deregisters the response handler"""
@@ -287,9 +288,12 @@ def __call__(self, request):
287288 # by the 401 handler
288289 host = urlparse (request .url ).hostname
289290
290- auth_header = self .generate_request_header (None , host , is_preemptive = True )
291+ auth_header = self .generate_request_header (None , host ,
292+ is_preemptive = True )
291293
292- log .debug ("HTTPKerberosAuth: Preemptive Authorization header: {0}" .format (auth_header ))
294+ log .debug (
295+ "HTTPKerberosAuth: Preemptive Authorization header: {0}"
296+ .format (auth_header ))
293297
294298 request .headers ['Authorization' ] = auth_header
295299
0 commit comments