From 9baeccce9678c035e45f87f6a9023feaeabed279 Mon Sep 17 00:00:00 2001 From: Matthew Sullivan Date: Wed, 23 Dec 2015 17:29:28 +0000 Subject: [PATCH 1/4] Add Basic Auth support --- splunklib/binding.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/splunklib/binding.py b/splunklib/binding.py index 426973297..e094839a4 100644 --- a/splunklib/binding.py +++ b/splunklib/binding.py @@ -33,6 +33,7 @@ import sys import Cookie +from base64 import b64encode from datetime import datetime from functools import wraps from StringIO import StringIO @@ -471,6 +472,7 @@ def __init__(self, handler=None, **kwargs): self.namespace = namespace(**kwargs) self.username = kwargs.get("username", "") self.password = kwargs.get("password", "") + self.basic = kwargs.get("basic", False) self.autologin = kwargs.get("autologin", False) # Store any cookies in the self.http._cookies dict @@ -507,6 +509,9 @@ def _auth_headers(self): """ if self.has_cookies(): return [("Cookie", _make_cookie_header(self.get_cookies().items()))] + elif self.basic and (self.username and self.password): + token = 'Basic %s' % b64encode("%s:%s" % (self.username, self.password)) + return [("Authorization", token)] elif self.token is _NoAuthenticationToken: return [] else: @@ -838,6 +843,11 @@ def login(self): # logged in. return + if self.basic and (self.username and self.password): + # Basic auth mode requested, so this method is a nop as long + # as credentials were passed in. + return + # Only try to get a token and updated cookie if username & password are specified try: response = self.http.post( From 9ee9b8b0b3c93100681dc1ef7621f94401816fa3 Mon Sep 17 00:00:00 2001 From: Matthew Sullivan Date: Wed, 23 Dec 2015 18:24:33 +0000 Subject: [PATCH 2/4] First pass at basic auth tests --- tests/test_binding.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_binding.py b/tests/test_binding.py index 2a3f079bc..3b44d4e1a 100755 --- a/tests/test_binding.py +++ b/tests/test_binding.py @@ -686,6 +686,29 @@ def test_namespace(self): def test_namespace_fails(self): self.assertRaises(ValueError, binding.namespace, sharing="gobble") +class TestBasicAuthentication(BindingTestCase): + def test_user_provided_credentials(self): + opts = self.opts.kwargs.copy() + opts["basic"] = True + opts["username"] = "boris the mad baboon" + opts["password"] = "nothing real" + + newContext = binding.Context(**opts) + response = newContext.get("/services") + self.assertEqual(response.status, 200) + + socket = newContext.connect() + socket.write("POST %s HTTP/1.1\r\n" % \ + self.context._abspath("some/path/to/post/to")) + socket.write("Host: %s:%s\r\n" % \ + (self.context.host, self.context.port)) + socket.write("Accept-Encoding: identity\r\n") + socket.write("Authorization: %s\r\n" % \ + self.context.token) + socket.write("X-Splunk-Input-Mode: Streaming\r\n") + socket.write("\r\n") + socket.close() + class TestTokenAuthentication(BindingTestCase): def test_preexisting_token(self): token = self.context.token From 413dad6273a7d56ca9bc8ad0061ad18fbdeb34b9 Mon Sep 17 00:00:00 2001 From: Matthew Sullivan Date: Wed, 23 Dec 2015 19:17:34 +0000 Subject: [PATCH 3/4] Another attempt at unit tests --- tests/test_binding.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/tests/test_binding.py b/tests/test_binding.py index 3b44d4e1a..f723399ce 100755 --- a/tests/test_binding.py +++ b/tests/test_binding.py @@ -686,28 +686,30 @@ def test_namespace(self): def test_namespace_fails(self): self.assertRaises(ValueError, binding.namespace, sharing="gobble") -class TestBasicAuthentication(BindingTestCase): - def test_user_provided_credentials(self): +class TestBasicAuthentication(unittest.TestCase): + def setUp(self): + self.opts = testlib.parse([], {}, ".splunkrc") opts = self.opts.kwargs.copy() opts["basic"] = True opts["username"] = "boris the mad baboon" opts["password"] = "nothing real" - newContext = binding.Context(**opts) - response = newContext.get("/services") - self.assertEqual(response.status, 200) + self.context = binding.connect(**opts) + import splunklib.client as client + service = client.Service(**opts) - socket = newContext.connect() - socket.write("POST %s HTTP/1.1\r\n" % \ - self.context._abspath("some/path/to/post/to")) - socket.write("Host: %s:%s\r\n" % \ - (self.context.host, self.context.port)) - socket.write("Accept-Encoding: identity\r\n") - socket.write("Authorization: %s\r\n" % \ - self.context.token) - socket.write("X-Splunk-Input-Mode: Streaming\r\n") - socket.write("\r\n") - socket.close() + if getattr(unittest.TestCase, 'assertIsNotNone', None) is None: + def assertIsNotNone(self, obj, msg=None): + if obj is None: + raise self.failureException, (msg or '%r is not None' % obj) + def test_basic_in_auth_headers(self): + self.assertIsNotNone(self.context._auth_headers) + self.assertNotEqual(self.context._auth_headers, []) + self.assertEqual(len(self.context._auth_headers), 1) + self.assertEqual(len(self.context._auth_headers), 1) + self.assertEqual(self.context._auth_headers[0][0], "Authorization") + self.assertEqual(self.context._auth_headers[0][1][:6], "Basic ") + self.assertEqual(self.context.get("/services").status, 200) class TestTokenAuthentication(BindingTestCase): def test_preexisting_token(self): From 8df46159426987ff6e8f11505da61d0dac720d3f Mon Sep 17 00:00:00 2001 From: Matthew Sullivan Date: Wed, 23 Dec 2015 19:31:30 +0000 Subject: [PATCH 4/4] Gah, spacing --- tests/test_binding.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/test_binding.py b/tests/test_binding.py index f723399ce..e27699fe2 100755 --- a/tests/test_binding.py +++ b/tests/test_binding.py @@ -698,11 +698,12 @@ def setUp(self): import splunklib.client as client service = client.Service(**opts) - if getattr(unittest.TestCase, 'assertIsNotNone', None) is None: - def assertIsNotNone(self, obj, msg=None): - if obj is None: - raise self.failureException, (msg or '%r is not None' % obj) - def test_basic_in_auth_headers(self): + if getattr(unittest.TestCase, 'assertIsNotNone', None) is None: + def assertIsNotNone(self, obj, msg=None): + if obj is None: + raise self.failureException, (msg or '%r is not None' % obj) + + def test_basic_in_auth_headers(self): self.assertIsNotNone(self.context._auth_headers) self.assertNotEqual(self.context._auth_headers, []) self.assertEqual(len(self.context._auth_headers), 1)