Skip to content

Commit 9dbee0e

Browse files
committed
Abstract GSSError handling into a decorator to simplify internal code
1 parent 4b1b0d2 commit 9dbee0e

File tree

1 file changed

+38
-21
lines changed

1 file changed

+38
-21
lines changed

httpx_gssapi/gssapi_.py

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import re
22
import logging
3-
from typing import Generator, Optional, List
3+
from functools import wraps
4+
from typing import Generator, Optional, List, Any
45

56
from base64 import b64encode, b64decode
67

@@ -58,6 +59,33 @@ def _sanitize_response(response: Response):
5859
response.headers[header] = headers[header]
5960

6061

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+
6189
class HTTPSPNEGOAuth(Auth):
6290
"""Attaches HTTP GSSAPI Authentication to the given Request object.
6391
@@ -180,6 +208,7 @@ def handle_mutual_auth(self, response: Response):
180208
log.error("handle_other(): Mutual authentication failed")
181209
raise MutualAuthenticationError(response=response)
182210

211+
@_handle_gsserror(gss_stage='stepping', result=SPNEGOExchangeError)
183212
def generate_request_header(self,
184213
host: str,
185214
response: Response = None) -> str:
@@ -189,18 +218,11 @@ def generate_request_header(self,
189218
If any GSSAPI step fails, raise SPNEGOExchangeError
190219
with failure detail.
191220
"""
192-
gss_stage = "initiating context"
193-
try:
194-
self.context[host] = self._make_context(host)
221+
self.context[host] = self._make_context(host)
195222

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()}"
204226

205227
def authenticate_user(self, response: Response) -> Request:
206228
"""Handles user authentication with GSSAPI"""
@@ -215,6 +237,7 @@ def authenticate_user(self, response: Response) -> Request:
215237

216238
return response.request
217239

240+
@_handle_gsserror(gss_stage="stepping", result=False)
218241
def authenticate_server(self, response: Response) -> bool:
219242
"""
220243
Uses GSSAPI to authenticate the server.
@@ -224,19 +247,13 @@ def authenticate_server(self, response: Response) -> bool:
224247
auth_header = _negotiate_value(response)
225248
log.debug(f"authenticate_server(): Authenticate header: {auth_header}")
226249

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)
236252

237253
log.debug("authenticate_server(): authentication successful")
238254
return True
239255

256+
@_handle_gsserror(gss_stage="initializing", result=SPNEGOExchangeError)
240257
def _make_context(self, host: str) -> gssapi.SecurityContext:
241258
"""
242259
Create a GSSAPI security context for handling the authentication.

0 commit comments

Comments
 (0)