Merge pull request #70 from nitzmahone/principal_and_realm_override

add support for principal and realm override
This commit is contained in:
Cory Benfield 2016-05-03 09:06:29 +01:00
commit d824b8c538
3 changed files with 111 additions and 13 deletions

View File

@ -65,7 +65,14 @@ authentication, you can do that as well:
Preemptive Authentication
-------------------------
``HTTPKerberosAuth`` can be forced to preemptively initiate the Kerberos GSS exchange and present a Kerberos ticket on the initial request (and all subsequent). By default, authentication only occurs after a ``401 Unauthorized`` response containing a Kerberos or Negotiate challenge is received from the origin server. This can cause mutual authentication failures for hosts that use a persistent connection (eg, Windows/WinRM), as no Kerberos challenges are sent after the initial auth handshake. This behavior can be altered by setting ``force_preemptive=True``:
``HTTPKerberosAuth`` can be forced to preemptively initiate the Kerberos
GSS exchange and present a Kerberos ticket on the initial request (and all
subsequent). By default, authentication only occurs after a
``401 Unauthorized`` response containing a Kerberos or Negotiate challenge
is received from the origin server. This can cause mutual authentication
failures for hosts that use a persistent connection (eg, Windows/WinRM), as
no Kerberos challenges are sent after the initial auth handshake. This
behavior can be altered by setting ``force_preemptive=True``:
.. code-block:: pycon
@ -75,6 +82,41 @@ Preemptive Authentication
>>> r = requests.get("https://windows.example.org/wsman", auth=kerberos_auth)
...
Hostname Override
-----------------
If communicating with a host whose DNS name doesn't match its
kerberos hostname (eg, behind a content switch or load balancer),
the hostname used for the Kerberos GSS exchange can be overridden by
setting the ``hostname_override`` arg:
.. code-block:: pycon
>>> import requests
>>> from requests_kerberos import HTTPKerberosAuth, REQUIRED
>>> kerberos_auth = HTTPKerberosAuth(hostname_override="internalhost.local")
>>> r = requests.get("https://externalhost.example.org/", auth=kerberos_auth)
...
Explicit Principal
------------------
``HTTPKerberosAuth`` normally uses the default principal (ie, the user for
whom you last ran ``kinit`` or ``kswitch``, or an SSO credential if
applicable). However, an explicit principal can be specified, which will
cause Kerberos to look for a matching credential cache for the named user.
This feature depends on OS support for collection-type credential caches,
as well as working principal support in pykerberos (it is broken in many
builds). An explicit principal can be specified with the ``principal`` arg:
.. code-block:: pycon
>>> import requests
>>> from requests_kerberos import HTTPKerberosAuth, REQUIRED
>>> kerberos_auth = HTTPKerberosAuth(principal="user@REALM")
>>> r = requests.get("http://example.org", auth=kerberos_auth)
...
Logging
-------

View File

@ -84,13 +84,16 @@ class HTTPKerberosAuth(AuthBase):
object."""
def __init__(
self, mutual_authentication=REQUIRED,
service="HTTP", delegate=False, force_preemptive=False):
service="HTTP", delegate=False, force_preemptive=False,
principal=None, hostname_override=None):
self.context = {}
self.mutual_authentication = mutual_authentication
self.delegate = delegate
self.pos = None
self.service = service
self.force_preemptive = force_preemptive
self.principal = principal
self.hostname_override = hostname_override
def generate_request_header(self, response, host, is_preemptive=False):
"""
@ -108,8 +111,15 @@ class HTTPKerberosAuth(AuthBase):
try:
kerb_stage = "authGSSClientInit()"
result, self.context[host] = kerberos.authGSSClientInit(
"{0}@{1}".format(self.service, host), gssflags=gssflags)
# contexts still need to be stored by host, but hostname_override
# allows use of an arbitrary hostname for the kerberos exchange
# (eg, in cases of aliased hosts, internal vs external, CNAMEs
# w/ name-based HTTP hosting)
kerb_host = self.hostname_override if self.hostname_override is not None else host
kerb_spn = "{0}@{1}".format(self.service, kerb_host)
result, self.context[host] = kerberos.authGSSClientInit(kerb_spn,
gssflags=gssflags, principal=self.principal)
if result < 1:
raise EnvironmentError(result, kerb_stage)

View File

@ -119,7 +119,8 @@ class KerberosTestCase(unittest.TestCase):
"HTTP@www.example.org",
gssflags=(
kerberos.GSS_C_MUTUAL_FLAG |
kerberos.GSS_C_SEQUENCE_FLAG))
kerberos.GSS_C_SEQUENCE_FLAG),
principal=None)
clientStep_continue.assert_called_with("CTX", "token")
clientResponse.assert_called_with("CTX")
@ -140,7 +141,8 @@ class KerberosTestCase(unittest.TestCase):
"HTTP@www.example.org",
gssflags=(
kerberos.GSS_C_MUTUAL_FLAG |
kerberos.GSS_C_SEQUENCE_FLAG))
kerberos.GSS_C_SEQUENCE_FLAG),
principal=None)
self.assertFalse(clientStep_continue.called)
self.assertFalse(clientResponse.called)
@ -161,7 +163,8 @@ class KerberosTestCase(unittest.TestCase):
"HTTP@www.example.org",
gssflags=(
kerberos.GSS_C_MUTUAL_FLAG |
kerberos.GSS_C_SEQUENCE_FLAG))
kerberos.GSS_C_SEQUENCE_FLAG),
principal=None)
clientStep_error.assert_called_with("CTX", "token")
self.assertFalse(clientResponse.called)
@ -205,7 +208,8 @@ class KerberosTestCase(unittest.TestCase):
"HTTP@www.example.org",
gssflags=(
kerberos.GSS_C_MUTUAL_FLAG |
kerberos.GSS_C_SEQUENCE_FLAG))
kerberos.GSS_C_SEQUENCE_FLAG),
principal=None)
clientStep_continue.assert_called_with("CTX", "token")
clientResponse.assert_called_with("CTX")
@ -249,7 +253,8 @@ class KerberosTestCase(unittest.TestCase):
"HTTP@www.example.org",
gssflags=(
kerberos.GSS_C_MUTUAL_FLAG |
kerberos.GSS_C_SEQUENCE_FLAG))
kerberos.GSS_C_SEQUENCE_FLAG),
principal=None)
clientStep_continue.assert_called_with("CTX", "token")
clientResponse.assert_called_with("CTX")
@ -480,7 +485,8 @@ class KerberosTestCase(unittest.TestCase):
"HTTP@www.example.org",
gssflags=(
kerberos.GSS_C_MUTUAL_FLAG |
kerberos.GSS_C_SEQUENCE_FLAG))
kerberos.GSS_C_SEQUENCE_FLAG),
principal=None)
clientStep_continue.assert_called_with("CTX", "token")
clientResponse.assert_called_with("CTX")
@ -529,7 +535,8 @@ class KerberosTestCase(unittest.TestCase):
"HTTP@www.example.org",
gssflags=(
kerberos.GSS_C_MUTUAL_FLAG |
kerberos.GSS_C_SEQUENCE_FLAG))
kerberos.GSS_C_SEQUENCE_FLAG),
principal=None)
clientStep_continue.assert_called_with("CTX", "token")
clientResponse.assert_called_with("CTX")
@ -548,7 +555,8 @@ class KerberosTestCase(unittest.TestCase):
"barfoo@www.example.org",
gssflags=(
kerberos.GSS_C_MUTUAL_FLAG |
kerberos.GSS_C_SEQUENCE_FLAG))
kerberos.GSS_C_SEQUENCE_FLAG),
principal=None)
def test_delegation(self):
with patch.multiple('kerberos',
@ -591,10 +599,48 @@ class KerberosTestCase(unittest.TestCase):
gssflags=(
kerberos.GSS_C_MUTUAL_FLAG |
kerberos.GSS_C_SEQUENCE_FLAG |
kerberos.GSS_C_DELEG_FLAG))
kerberos.GSS_C_DELEG_FLAG),
principal=None
)
clientStep_continue.assert_called_with("CTX", "token")
clientResponse.assert_called_with("CTX")
def test_principal_override(self):
with patch.multiple(kerberos_module_name,
authGSSClientInit=clientInit_complete,
authGSSClientResponse=clientResponse,
authGSSClientStep=clientStep_continue):
response = requests.Response()
response.url = "http://www.example.org/"
response.headers = {'www-authenticate': 'negotiate token'}
host = urlparse(response.url).hostname
auth = requests_kerberos.HTTPKerberosAuth(principal="user@REALM")
auth.generate_request_header(response, host),
clientInit_complete.assert_called_with(
"HTTP@www.example.org",
gssflags=(
kerberos.GSS_C_MUTUAL_FLAG |
kerberos.GSS_C_SEQUENCE_FLAG),
principal="user@REALM")
def test_realm_override(self):
with patch.multiple(kerberos_module_name,
authGSSClientInit=clientInit_complete,
authGSSClientResponse=clientResponse,
authGSSClientStep=clientStep_continue):
response = requests.Response()
response.url = "http://www.example.org/"
response.headers = {'www-authenticate': 'negotiate token'}
host = urlparse(response.url).hostname
auth = requests_kerberos.HTTPKerberosAuth(hostname_override="otherhost.otherdomain.org")
auth.generate_request_header(response, host),
clientInit_complete.assert_called_with(
"HTTP@otherhost.otherdomain.org",
gssflags=(
kerberos.GSS_C_MUTUAL_FLAG |
kerberos.GSS_C_SEQUENCE_FLAG),
principal=None)
if __name__ == '__main__':
unittest.main()