Skip to content

Commit

Permalink
Add support for specifing an explicit GSSAPI mech
Browse files Browse the repository at this point in the history
Add support for passing an explicit mech through to gssapi's
`SecurityContext` constructor. This allows overriding the auto-detected
mechanism, and enabling support for RFC4178 SPNEGO.

Fixes #18
  • Loading branch information
optiz0r authored and frozencemetery committed Feb 18, 2020
1 parent dba7c1c commit c9e429c
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 15 deletions.
21 changes: 21 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,27 @@ applicable). However, an explicit credential can be in instead, if desired.
>>> r = requests.get("http://example.org", auth=gssapi_auth)
...
Explicit Mechanism
------------------

``HTTPSPNEGOAuth`` normally lets the underlying ``gssapi`` library decide which
negotiation mechanism to use. However, an explicit mechanism can be used instead
if desired. The ``mech`` parameter will be passed straight through to ``gssapi``
without interference. It is expected to be an instance of ``gssapi.mechs.Mechanism``.

.. code-block:: python
>>> import gssapi
>>> import requests
>>> from requests_gssapi import HTTPSPNEGOAuth
>>> try:
... spnego = gssapi,mechs.Mechanism.from_sasl_name("SPNEGO")
... except AttributeError:
... spnego = gssapi.OID.from_int_seq("1.3.6.1.5.5.2")
>>> gssapi_auth = HTTPSPNEGOAuth(mech=spnego)
>>> r = requests.get("http://example.org", auth=gssapi_auth)
...
Delegation
----------

Expand Down
8 changes: 6 additions & 2 deletions requests_gssapi/gssapi_.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,20 +98,24 @@ class HTTPSPNEGOAuth(AuthBase):
`creds` is GSSAPI credentials (gssapi.Credentials) to use for negotiation.
Default is `None`.
`mech` is GSSAPI Mechanism (gssapi.Mechanism) to use for negotiation.
Default is `None`
`sanitize_mutual_error_response` controls whether we should clean up
server responses. See the `SanitizedResponse` class.
"""
def __init__(self, mutual_authentication=DISABLED, target_name="HTTP",
delegate=False, opportunistic_auth=False, creds=None,
sanitize_mutual_error_response=True):
mech=None, sanitize_mutual_error_response=True):
self.context = {}
self.pos = None
self.mutual_authentication = mutual_authentication
self.target_name = target_name
self.delegate = delegate
self.opportunistic_auth = opportunistic_auth
self.creds = creds
self.mech = mech
self.sanitize_mutual_error_response = sanitize_mutual_error_response

def generate_request_header(self, response, host, is_preemptive=False):
Expand Down Expand Up @@ -139,7 +143,7 @@ def generate_request_header(self, response, host, is_preemptive=False):
self.target_name, gssapi.NameType.hostbased_service)
self.context[host] = gssapi.SecurityContext(
usage="initiate", flags=gssflags, name=self.target_name,
creds=self.creds)
creds=self.creds, mech=self.mech)

gss_stage = "stepping context"
if is_preemptive:
Expand Down
45 changes: 32 additions & 13 deletions test_requests_gssapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def test_generate_request_header(self):
b64_negotiate_response)
fake_init.assert_called_with(
name=gssapi_name("[email protected]"),
creds=None, flags=gssflags, usage="initiate")
creds=None, mech=None, flags=gssflags, usage="initiate")
fake_resp.assert_called_with(b"token")

def test_generate_request_header_init_error(self):
Expand All @@ -121,7 +121,7 @@ def test_generate_request_header_init_error(self):
auth.generate_request_header, response, host)
fake_init.assert_called_with(
name=gssapi_name("[email protected]"),
usage="initiate", flags=gssflags, creds=None)
usage="initiate", flags=gssflags, creds=None, mech=None)

def test_generate_request_header_step_error(self):
with patch.multiple("gssapi.SecurityContext", __init__=fake_init,
Expand All @@ -135,7 +135,7 @@ def test_generate_request_header_step_error(self):
auth.generate_request_header, response, host)
fake_init.assert_called_with(
name=gssapi_name("[email protected]"),
usage="initiate", flags=gssflags, creds=None)
usage="initiate", flags=gssflags, creds=None, mech=None)
fail_resp.assert_called_with(b"token")

def test_authenticate_user(self):
Expand Down Expand Up @@ -172,7 +172,7 @@ def test_authenticate_user(self):
raw.release_conn.assert_called_with()
fake_init.assert_called_with(
name=gssapi_name("[email protected]"),
flags=gssflags, usage="initiate", creds=None)
flags=gssflags, usage="initiate", creds=None, mech=None)
fake_resp.assert_called_with(b"token")

def test_handle_401(self):
Expand Down Expand Up @@ -209,7 +209,7 @@ def test_handle_401(self):
raw.release_conn.assert_called_with()
fake_init.assert_called_with(
name=gssapi_name("[email protected]"),
creds=None, flags=gssflags, usage="initiate")
creds=None, mech=None, flags=gssflags, usage="initiate")
fake_resp.assert_called_with(b"token")

def test_authenticate_server(self):
Expand Down Expand Up @@ -448,7 +448,7 @@ def test_handle_response_401(self):
raw.release_conn.assert_called_with()
fake_init.assert_called_with(
name=gssapi_name("[email protected]"),
usage="initiate", flags=gssflags, creds=None)
usage="initiate", flags=gssflags, creds=None, mech=None)
fake_resp.assert_called_with(b"token")

def test_handle_response_401_rejected(self):
Expand Down Expand Up @@ -491,7 +491,7 @@ def connection_send(self, *args, **kwargs):
raw.release_conn.assert_called_with()
fake_init.assert_called_with(
name=gssapi_name("[email protected]"),
usage="initiate", flags=gssflags, creds=None)
usage="initiate", flags=gssflags, creds=None, mech=None)
fake_resp.assert_called_with(b"token")

def test_generate_request_header_custom_service(self):
Expand All @@ -505,7 +505,7 @@ def test_generate_request_header_custom_service(self):
auth.generate_request_header(response, host),
fake_init.assert_called_with(
name=gssapi_name("[email protected]"),
usage="initiate", flags=gssflags, creds=None)
usage="initiate", flags=gssflags, creds=None, mech=None)
fake_resp.assert_called_with(b"token")

def test_delegation(self):
Expand Down Expand Up @@ -543,7 +543,7 @@ def test_delegation(self):
raw.release_conn.assert_called_with()
fake_init.assert_called_with(
name=gssapi_name("[email protected]"),
usage="initiate", flags=gssdelegflags, creds=None)
usage="initiate", flags=gssdelegflags, creds=None, mech=None)
fake_resp.assert_called_with(b"token")

def test_principal_override(self):
Expand All @@ -561,7 +561,8 @@ def test_principal_override(self):
name=gssapi_name("user@REALM"))
fake_init.assert_called_with(
name=gssapi_name("[email protected]"),
usage="initiate", flags=gssflags, creds=b"fake creds")
usage="initiate", flags=gssflags,
creds=b"fake creds", mech=None)

def test_realm_override(self):
with patch.multiple("gssapi.SecurityContext", __init__=fake_init,
Expand All @@ -575,7 +576,7 @@ def test_realm_override(self):
auth.generate_request_header(response, host)
fake_init.assert_called_with(
name=gssapi_name("[email protected]"),
usage="initiate", flags=gssflags, creds=None)
usage="initiate", flags=gssflags, creds=None, mech=None)
fake_resp.assert_called_with(b"token")

def test_opportunistic_auth(self):
Expand Down Expand Up @@ -604,7 +605,25 @@ def test_explicit_creds(self):
auth.generate_request_header(response, host)
fake_init.assert_called_with(
name=gssapi_name("[email protected]"),
usage="initiate", flags=gssflags, creds=b"fake creds")
usage="initiate", flags=gssflags,
creds=b"fake creds", mech=None)
fake_resp.assert_called_with(b"token")

def test_explicit_mech(self):
with patch.multiple("gssapi.Credentials", __new__=fake_creds), \
patch.multiple("gssapi.SecurityContext", __init__=fake_init,
step=fake_resp):
response = requests.Response()
response.url = "http://www.example.org/"
response.headers = {'www-authenticate': b64_negotiate_token}
host = urlparse(response.url).hostname
fake_mech = b'fake mech'
auth = requests_gssapi.HTTPSPNEGOAuth(mech=fake_mech)
auth.generate_request_header(response, host)
fake_init.assert_called_with(
name=gssapi_name("[email protected]"),
usage="initiate", flags=gssflags,
creds=None, mech=b'fake mech')
fake_resp.assert_called_with(b"token")

def test_target_name(self):
Expand All @@ -619,7 +638,7 @@ def test_target_name(self):
auth.generate_request_header(response, host)
fake_init.assert_called_with(
name=gssapi_name("[email protected]"),
usage="initiate", flags=gssflags, creds=None)
usage="initiate", flags=gssflags, creds=None, mech=None)
fake_resp.assert_called_with(b"token")


Expand Down

0 comments on commit c9e429c

Please sign in to comment.