1
1
import re
2
2
import logging
3
- from typing import Generator , Optional , List
3
+ from functools import wraps
4
+ from typing import Generator , Optional , List , Any
4
5
5
6
from base64 import b64encode , b64decode
6
7
@@ -58,6 +59,33 @@ def _sanitize_response(response: Response):
58
59
response .headers [header ] = headers [header ]
59
60
60
61
62
+ def _handle_gsserror (* , gss_stage : str , result : Any ):
63
+ """
64
+ Decorator to handle GSSErrors and properly log them against the decorated
65
+ function's name.
66
+
67
+ :param gss_stage:
68
+ Name of GSS stage that the function is handling. Typically either
69
+ 'initializing' or 'stepping'.
70
+ :param result:
71
+ The result to return if a GSSError is raised. If it's an Exception
72
+ type, then it will be raised with the logged message.
73
+ """
74
+ def _decor (func ):
75
+ @wraps (func )
76
+ def _wrapper (* args , ** kwargs ):
77
+ try :
78
+ return func (* args , ** kwargs )
79
+ except gssapi .exceptions .GSSError as error :
80
+ msg = f"{ gss_stage } context failed: { error .gen_message ()} "
81
+ log .exception (f"{ func .__name__ } (): { msg } " )
82
+ if isinstance (result , type ) and issubclass (result , Exception ):
83
+ raise result (msg )
84
+ return result
85
+ return _wrapper
86
+ return _decor
87
+
88
+
61
89
class HTTPSPNEGOAuth (Auth ):
62
90
"""Attaches HTTP GSSAPI Authentication to the given Request object.
63
91
@@ -180,6 +208,7 @@ def handle_mutual_auth(self, response: Response):
180
208
log .error ("handle_other(): Mutual authentication failed" )
181
209
raise MutualAuthenticationError (response = response )
182
210
211
+ @_handle_gsserror (gss_stage = 'stepping' , result = SPNEGOExchangeError )
183
212
def generate_request_header (self ,
184
213
host : str ,
185
214
response : Response = None ) -> str :
@@ -189,18 +218,11 @@ def generate_request_header(self,
189
218
If any GSSAPI step fails, raise SPNEGOExchangeError
190
219
with failure detail.
191
220
"""
192
- gss_stage = "initiating context"
193
- try :
194
- self .context [host ] = self ._make_context (host )
221
+ self .context [host ] = self ._make_context (host )
195
222
196
- gss_stage = "stepping context"
197
- token = _negotiate_value (response ) if response else None
198
- gss_resp = self .context [host ].step (token )
199
- return f"Negotiate { b64encode (gss_resp ).decode ()} "
200
- except GSSError as error :
201
- msg = f"{ gss_stage } failed: { error .gen_message ()} "
202
- log .exception (f"generate_request_header(): { msg } " )
203
- raise SPNEGOExchangeError (msg )
223
+ token = _negotiate_value (response ) if response else None
224
+ gss_resp = self .context [host ].step (token )
225
+ return f"Negotiate { b64encode (gss_resp ).decode ()} "
204
226
205
227
def authenticate_user (self , response : Response ) -> Request :
206
228
"""Handles user authentication with GSSAPI"""
@@ -215,6 +237,7 @@ def authenticate_user(self, response: Response) -> Request:
215
237
216
238
return response .request
217
239
240
+ @_handle_gsserror (gss_stage = "stepping" , result = False )
218
241
def authenticate_server (self , response : Response ) -> bool :
219
242
"""
220
243
Uses GSSAPI to authenticate the server.
@@ -224,19 +247,13 @@ def authenticate_server(self, response: Response) -> bool:
224
247
auth_header = _negotiate_value (response )
225
248
log .debug (f"authenticate_server(): Authenticate header: { auth_header } " )
226
249
227
- try :
228
- # If the handshake isn't complete here, nothing we can do
229
- self .context [response .url .host ].step (auth_header )
230
- except gssapi .exceptions .GSSError as error :
231
- log .exception (
232
- f"authenticate_server(): context stepping "
233
- f"failed: { error .gen_message ()} "
234
- )
235
- return False
250
+ # If the handshake isn't complete here, nothing we can do
251
+ self .context [response .url .host ].step (auth_header )
236
252
237
253
log .debug ("authenticate_server(): authentication successful" )
238
254
return True
239
255
256
+ @_handle_gsserror (gss_stage = "initializing" , result = SPNEGOExchangeError )
240
257
def _make_context (self , host : str ) -> gssapi .SecurityContext :
241
258
"""
242
259
Create a GSSAPI security context for handling the authentication.
0 commit comments