From 1cd807640ad8effa2b89c24ca54ea02d78289306 Mon Sep 17 00:00:00 2001 From: trek333 Date: Fri, 9 Sep 2022 13:30:20 -0500 Subject: [PATCH 01/32] Added test case for cookie persistence --- tests/test_binding.py | 50 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/test_binding.py b/tests/test_binding.py index c101b19c..01d7e138 100755 --- a/tests/test_binding.py +++ b/tests/test_binding.py @@ -491,6 +491,56 @@ def test_handlers(self): body = context.get(path).body.read() self.assertTrue(isatom(body)) +def urllib2_insert_cookie_handler(url, message, **kwargs): + method = message['method'].lower() + data = message.get('body', b"") if method == 'post' else None + headers = dict(message.get('headers', [])) + req = Request(url, data, headers) + try: + # If running Python 2.7.9+, disable SSL certificate validation + if sys.version_info >= (2, 7, 9): + response = urlopen(req, context=ssl._create_unverified_context()) + else: + response = urlopen(req) + except HTTPError as response: + pass # Propagate HTTP errors via the returned response message + + # Mimic the insertion of 3rd party cookies into the response. + # An example is "sticky session"/"insert cookie" persistence + # of a load balancer for a SHC. + header_list = response.getheaders() + header_list.append(("Set-Cookie", "BIGipServer_splunk-shc-8089=1234567890.12345.0000; path=/; Httponly; Secure")) + header_list.append(("Set-Cookie", "home_made=yummy")) + + return { + 'status': response.code, + 'reason': response.msg, + 'headers': header_list, + 'body': BytesIO(response.read()) + } + +class TestCookiePersistence(testlib.SDKTestCase): + # Verify persistence of 3rd party inserted cookies. + def test_3rdPartyInsertedCookiePersistence(self): + paths = ["/services", "authentication/users", + "search/jobs"] + logging.debug("Connecting with urllib2_insert_cookie_handler %s", urllib2_insert_cookie_handler) + context = binding.connect( + handler=urllib2_insert_cookie_handler, + **self.opts.kwargs) + + persisted_cookies = context.get_cookies() + + splunk_token_found = False + for k, v in persisted_cookies.items(): + if k[:8] == "splunkd_": + splunk_token_found = True + break + + self.assertEqual(splunk_token_found, True) + self.assertEqual(persisted_cookies['BIGipServer_splunk-shc-8089'], "1234567890.12345.0000") + self.assertEqual(persisted_cookies['home_made'], "yummy") + @pytest.mark.smoke class TestLogout(BindingTestCase): def test_logout(self): From a24bad04daa868f0b21ccf117b1b791bc389c734 Mon Sep 17 00:00:00 2001 From: akaila-splunk Date: Thu, 6 Oct 2022 15:35:25 +0530 Subject: [PATCH 02/32] added acl_update method - added acl_update method to update ACL properties of an entity --- splunklib/client.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/splunklib/client.py b/splunklib/client.py index cde39e95..d595b82c 100644 --- a/splunklib/client.py +++ b/splunklib/client.py @@ -1216,6 +1216,36 @@ def reload(self): self.post("_reload") return self + def acl_update(self, **kwargs): + """To update Access Control List (ACL) properties for an endpoint. + + :param kwargs: Additional entity-specific arguments (required). + + - "owner" (``string``): The Splunk username, such as "admin". A value of "nobody" means no specific user (required). + + - "sharing" (``string``): A mode that indicates how the resource is shared. The sharing mode can be "user", "app", "global", or "system" (required). + + :type kwargs: ``dict`` + + **Example**:: + + import splunklib.client as client + service = client.connect(...) + saved_search = service.saved_searches["name"] + saved_search.acl_update(sharing="app", owner="nobody", app="search", **{"perms.read": "admin, nobody"}) + """ + if "body" not in kwargs: + kwargs = {"body": {**kwargs}} + + if "sharing" not in kwargs["body"]: + raise ValueError("Required argument 'sharing' is missing.") + if "owner" not in kwargs["body"]: + raise ValueError("Required argument 'owner' is missing.") + + self.post("acl", **kwargs) + self.refresh() + return self + @property def state(self): """Returns the entity's state record. From 03a0b75227c830e15638090157188bf319d30ad2 Mon Sep 17 00:00:00 2001 From: akaila-splunk Date: Thu, 6 Oct 2022 15:35:59 +0530 Subject: [PATCH 03/32] added test cases for acl_update method --- tests/test_saved_search.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_saved_search.py b/tests/test_saved_search.py index c15921c0..d1f8f57c 100755 --- a/tests/test_saved_search.py +++ b/tests/test_saved_search.py @@ -223,6 +223,30 @@ def test_suppress(self): self.saved_search.unsuppress() self.assertEqual(self.saved_search['suppressed'], 0) + def test_acl(self): + self.assertEqual(self.saved_search.access["perms"], None) + self.saved_search.acl_update(sharing="app", owner="admin", app="search", **{"perms.read": "admin, nobody"}) + self.assertEqual(self.saved_search.access["owner"], "admin") + self.assertEqual(self.saved_search.access["app"], "search") + self.assertEqual(self.saved_search.access["sharing"], "app") + self.assertEqual(self.saved_search.access["perms"]["read"], ['admin', 'nobody']) + + def test_acl_fails_without_sharing(self): + self.assertRaisesRegex( + ValueError, + "Required argument 'sharing' is missing.", + self.saved_search.acl_update, + owner="admin", app="search", **{"perms.read": "admin, nobody"} + ) + + def test_acl_fails_without_owner(self): + self.assertRaisesRegex( + ValueError, + "Required argument 'owner' is missing.", + self.saved_search.acl_update, + sharing="app", app="search", **{"perms.read": "admin, nobody"} + ) + if __name__ == "__main__": try: import unittest2 as unittest From de0d56d5d8a1899a5f07872c3ca0e4e478b0f8ac Mon Sep 17 00:00:00 2001 From: akaila-splunk Date: Thu, 6 Oct 2022 15:59:25 +0530 Subject: [PATCH 04/32] Update client.py --- splunklib/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/splunklib/client.py b/splunklib/client.py index d595b82c..564a40f6 100644 --- a/splunklib/client.py +++ b/splunklib/client.py @@ -1235,7 +1235,7 @@ def acl_update(self, **kwargs): saved_search.acl_update(sharing="app", owner="nobody", app="search", **{"perms.read": "admin, nobody"}) """ if "body" not in kwargs: - kwargs = {"body": {**kwargs}} + kwargs = {"body": kwargs} if "sharing" not in kwargs["body"]: raise ValueError("Required argument 'sharing' is missing.") From e2a4d8c661bde480a377a0df8b4b4e0790f08363 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Fri, 7 Oct 2022 00:47:59 +0530 Subject: [PATCH 05/32] Update event_writer.py Updated file permissions --- splunklib/modularinput/event_writer.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 splunklib/modularinput/event_writer.py diff --git a/splunklib/modularinput/event_writer.py b/splunklib/modularinput/event_writer.py old mode 100755 new mode 100644 From 1af8e4050e4923995b2ebb20401dd36a19a79188 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Fri, 7 Oct 2022 12:31:48 +0530 Subject: [PATCH 06/32] test doc generation --- .github/workflows/release.yml | 19 ++++++++++--------- .github/workflows/test.yml | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4d11da59..7215383a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,7 +1,8 @@ name: Release on: - release: - types: [published] + [push] +# release: +# types: [published] jobs: publish: @@ -18,23 +19,23 @@ jobs: run: pip install twine - name: Build package run: python setup.py sdist - - name: Publish package to PyPI - uses: pypa/gh-action-pypi-publish@v1.3.1 - with: - user: __token__ - password: ${{ secrets.pypi_password }} +# - name: Publish package to PyPI +# uses: pypa/gh-action-pypi-publish@v1.3.1 +# with: +# user: __token__ +# password: ${{ secrets.pypi_password }} - name: Install tox run: pip install tox - name: Generate API docs run: | rm -rf ./docs/_build tox -e docs - cd ./docs/_build/html && zip -r ../docs_html.zip . -x ".*" -x "__MACOSX" +# cd ./docs/_build/html && zip -r ../docs_html.zip . -x ".*" -x "__MACOSX" - name : Docs Upload uses: actions/upload-artifact@v3 with: name: apidocs - path: docs/_build/docs_html.zip + path: docs/_build/html # Test upload # - name: Publish package to TestPyPI # uses: pypa/gh-action-pypi-publish@master diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 06278e29..be76ad2d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,7 +1,7 @@ name: Python CI on: - [ push, pull_request ] + [pull_request ] jobs: build: From 7b43f525d591bac013cba66c6394bd707f4763c8 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Fri, 7 Oct 2022 15:34:58 +0530 Subject: [PATCH 07/32] Update release.yml --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7215383a..1434d90a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,7 +34,7 @@ jobs: - name : Docs Upload uses: actions/upload-artifact@v3 with: - name: apidocs + name: python_sdk_docs path: docs/_build/html # Test upload # - name: Publish package to TestPyPI From 9e683c68767bc94284cadf211e56cf4341160580 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Fri, 7 Oct 2022 15:48:15 +0530 Subject: [PATCH 08/32] test doc generation --- .github/workflows/release.yml | 20 ++++++++++---------- .github/workflows/test.yml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4d11da59..ff9ca772 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,7 +1,8 @@ name: Release on: - release: - types: [published] + [push] +# release: +# types: [published] jobs: publish: @@ -18,23 +19,22 @@ jobs: run: pip install twine - name: Build package run: python setup.py sdist - - name: Publish package to PyPI - uses: pypa/gh-action-pypi-publish@v1.3.1 - with: - user: __token__ - password: ${{ secrets.pypi_password }} +# - name: Publish package to PyPI +# uses: pypa/gh-action-pypi-publish@v1.3.1 +# with: +# user: __token__ +# password: ${{ secrets.pypi_password }} - name: Install tox run: pip install tox - name: Generate API docs run: | rm -rf ./docs/_build tox -e docs - cd ./docs/_build/html && zip -r ../docs_html.zip . -x ".*" -x "__MACOSX" - name : Docs Upload uses: actions/upload-artifact@v3 with: - name: apidocs - path: docs/_build/docs_html.zip + name: python_sdk_docs + path: docs/_build/html # Test upload # - name: Publish package to TestPyPI # uses: pypa/gh-action-pypi-publish@master diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 06278e29..15be4909 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,7 +1,7 @@ name: Python CI on: - [ push, pull_request ] + [ pull_request ] jobs: build: From 1be1cc8aded5d5b8c25c20c4c41d65851c49554d Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Fri, 7 Oct 2022 15:51:04 +0530 Subject: [PATCH 09/32] reverting changes done for test purpose --- .github/workflows/release.yml | 15 +++++++-------- .github/workflows/test.yml | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ff9ca772..9309a311 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,8 +1,7 @@ name: Release on: - [push] -# release: -# types: [published] + release: + types: [published] jobs: publish: @@ -19,11 +18,11 @@ jobs: run: pip install twine - name: Build package run: python setup.py sdist -# - name: Publish package to PyPI -# uses: pypa/gh-action-pypi-publish@v1.3.1 -# with: -# user: __token__ -# password: ${{ secrets.pypi_password }} + - name: Publish package to PyPI + uses: pypa/gh-action-pypi-publish@v1.3.1 + with: + user: __token__ + password: ${{ secrets.pypi_password }} - name: Install tox run: pip install tox - name: Generate API docs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 15be4909..06278e29 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,7 +1,7 @@ name: Python CI on: - [ pull_request ] + [ push, pull_request ] jobs: build: From 0b0f6492ea58d0b7cf3b2c6d26222ba4601acca7 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Mon, 10 Oct 2022 11:55:18 +0530 Subject: [PATCH 10/32] revert workflow changes --- .gitconfig | 2 ++ .github/workflows/release.yml | 16 +++++++--------- .github/workflows/test.yml | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 .gitconfig diff --git a/.gitconfig b/.gitconfig new file mode 100644 index 00000000..4f73bb61 --- /dev/null +++ b/.gitconfig @@ -0,0 +1,2 @@ +[core] + filemode = true \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1434d90a..9309a311 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,8 +1,7 @@ name: Release on: - [push] -# release: -# types: [published] + release: + types: [published] jobs: publish: @@ -19,18 +18,17 @@ jobs: run: pip install twine - name: Build package run: python setup.py sdist -# - name: Publish package to PyPI -# uses: pypa/gh-action-pypi-publish@v1.3.1 -# with: -# user: __token__ -# password: ${{ secrets.pypi_password }} + - name: Publish package to PyPI + uses: pypa/gh-action-pypi-publish@v1.3.1 + with: + user: __token__ + password: ${{ secrets.pypi_password }} - name: Install tox run: pip install tox - name: Generate API docs run: | rm -rf ./docs/_build tox -e docs -# cd ./docs/_build/html && zip -r ../docs_html.zip . -x ".*" -x "__MACOSX" - name : Docs Upload uses: actions/upload-artifact@v3 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index be76ad2d..45af614a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,7 +1,7 @@ name: Python CI on: - [pull_request ] + [push, pull_request ] jobs: build: From b68b01475f26dae9fc02accaca6b03e8eba013c3 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Mon, 10 Oct 2022 12:00:14 +0530 Subject: [PATCH 11/32] sync --- .gitconfig | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitconfig b/.gitconfig index 4f73bb61..561dfa1d 100644 --- a/.gitconfig +++ b/.gitconfig @@ -1,2 +1,2 @@ [core] - filemode = true \ No newline at end of file + filemode = false \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 45af614a..06278e29 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,7 +1,7 @@ name: Python CI on: - [push, pull_request ] + [ push, pull_request ] jobs: build: From 2fa7723d00cbb85e8de34a8b13eb530e57b3d079 Mon Sep 17 00:00:00 2001 From: trek333 Date: Mon, 24 Oct 2022 10:05:32 -0500 Subject: [PATCH 12/32] fixed get headers logic --- tests/test_binding.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_binding.py b/tests/test_binding.py index 01d7e138..aa3a1391 100755 --- a/tests/test_binding.py +++ b/tests/test_binding.py @@ -508,7 +508,7 @@ def urllib2_insert_cookie_handler(url, message, **kwargs): # Mimic the insertion of 3rd party cookies into the response. # An example is "sticky session"/"insert cookie" persistence # of a load balancer for a SHC. - header_list = response.getheaders() + header_list = [(k, v) for k, v in response.info().items()] header_list.append(("Set-Cookie", "BIGipServer_splunk-shc-8089=1234567890.12345.0000; path=/; Httponly; Secure")) header_list.append(("Set-Cookie", "home_made=yummy")) From 296c154b5b917f84d71c329fafbb3c66a00d92f6 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Mon, 31 Oct 2022 16:04:33 +0530 Subject: [PATCH 13/32] Update test_utils.py added test case to check if any change in file-permission is made --- tests/test_utils.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_utils.py b/tests/test_utils.py index 5b6b712c..1db09b6b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,9 @@ from __future__ import absolute_import from tests import testlib +import unittest +import os + try: from utils import * except ImportError: @@ -76,6 +79,27 @@ def test_dslice_all_args(self): } self.assertTrue(expected == dslice(TEST_DICT, *test_args)) +class FilePermissionTest(unittest.TestCase): + + def setUp(self): + super(FilePermissionTest, self).setUp() + + def test_filePermissions(self): + + def checkFilePermissions(dir_path): + for file in os.listdir(dir_path): + if file.__contains__('pycache'): + continue + path = os.path.join(dir_path, file) + if os.path.isfile(path): + permission = oct(os.stat(path).st_mode) + self.assertEqual(permission, '0o100644') + else: + checkFilePermissions(path) + + dir_path = os.path.join('..', 'splunklib') + checkFilePermissions(dir_path) + if __name__ == "__main__": try: From 968f01e29ea06614281bc960bf9a9255b1132df9 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Mon, 31 Oct 2022 16:08:13 +0530 Subject: [PATCH 14/32] Delete .gitconfig --- .gitconfig | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .gitconfig diff --git a/.gitconfig b/.gitconfig deleted file mode 100644 index 561dfa1d..00000000 --- a/.gitconfig +++ /dev/null @@ -1,2 +0,0 @@ -[core] - filemode = false \ No newline at end of file From af6826214035de7db1edbe539064c26e0038bb77 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Fri, 4 Nov 2022 14:39:51 +0530 Subject: [PATCH 15/32] Update test_utils.py --- tests/test_utils.py | 62 +++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 1db09b6b..2e6bae19 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -10,14 +10,14 @@ raise Exception("Add the SDK repository to your PYTHONPATH to run the test cases " "(e.g., export PYTHONPATH=~/splunk-sdk-python.") - TEST_DICT = { - 'username':'admin', - 'password':'changeme', - 'port' : 8089, - 'host' : 'localhost', - 'scheme': 'https' - } + 'username': 'admin', + 'password': 'changeme', + 'port': 8089, + 'host': 'localhost', + 'scheme': 'https' +} + class TestUtils(testlib.SDKTestCase): def setUp(self): @@ -26,16 +26,16 @@ def setUp(self): # Test dslice when a dict is passed to change key names def test_dslice_dict_args(self): args = { - 'username':'user-name', - 'password':'new_password', - 'port': 'admin_port', - 'foo':'bar' - } + 'username': 'user-name', + 'password': 'new_password', + 'port': 'admin_port', + 'foo': 'bar' + } expected = { - 'user-name':'admin', - 'new_password':'changeme', - 'admin_port':8089 - } + 'user-name': 'admin', + 'new_password': 'changeme', + 'admin_port': 8089 + } self.assertTrue(expected == dslice(TEST_DICT, args)) # Test dslice when a list is passed @@ -46,44 +46,46 @@ def test_dslice_list_args(self): 'port', 'host', 'foo' - ] + ] expected = { - 'username':'admin', - 'password':'changeme', - 'port':8089, - 'host':'localhost' - } + 'username': 'admin', + 'password': 'changeme', + 'port': 8089, + 'host': 'localhost' + } self.assertTrue(expected == dslice(TEST_DICT, test_list)) # Test dslice when a single string is passed def test_dslice_arg(self): test_arg = 'username' expected = { - 'username':'admin' - } + 'username': 'admin' + } self.assertTrue(expected == dslice(TEST_DICT, test_arg)) # Test dslice using all three types of arguments def test_dslice_all_args(self): test_args = [ - {'username':'new_username'}, + {'username': 'new_username'}, ['password', - 'host'], + 'host'], 'port' ] expected = { - 'new_username':'admin', - 'password':'changeme', - 'host':'localhost', - 'port':8089 + 'new_username': 'admin', + 'password': 'changeme', + 'host': 'localhost', + 'port': 8089 } self.assertTrue(expected == dslice(TEST_DICT, *test_args)) + class FilePermissionTest(unittest.TestCase): def setUp(self): super(FilePermissionTest, self).setUp() + # Check for any change in the default file permission(i.e 644) for all files within splunklib def test_filePermissions(self): def checkFilePermissions(dir_path): From a3588e039ff884b47c900d0bebb9344f8e1cfe08 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Fri, 4 Nov 2022 17:35:36 +0530 Subject: [PATCH 16/32] Update test_utils.py --- tests/test_utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 2e6bae19..f0b39a1b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3,6 +3,7 @@ import unittest import os +import sys try: from utils import * @@ -95,7 +96,10 @@ def checkFilePermissions(dir_path): path = os.path.join(dir_path, file) if os.path.isfile(path): permission = oct(os.stat(path).st_mode) - self.assertEqual(permission, '0o100644') + if sys.version_info >= (2, 7, 9): + self.assertEqual(permission, '0o100644') + else : + self.assertEqual(permission, '0100644') else: checkFilePermissions(path) From f1fea495d42b8aa3b5e283153c5ecff584daeb08 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Fri, 4 Nov 2022 17:49:40 +0530 Subject: [PATCH 17/32] Update test_utils.py --- tests/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index f0b39a1b..1a362296 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -96,7 +96,7 @@ def checkFilePermissions(dir_path): path = os.path.join(dir_path, file) if os.path.isfile(path): permission = oct(os.stat(path).st_mode) - if sys.version_info >= (2, 7, 9): + if sys.version_info > (2, 7, 9): self.assertEqual(permission, '0o100644') else : self.assertEqual(permission, '0100644') From 80a123c02075b91be8fc7139e7c2a1bc25fc333e Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Fri, 4 Nov 2022 18:03:06 +0530 Subject: [PATCH 18/32] Update test_utils.py --- tests/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 1a362296..5eedbaba 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -96,7 +96,7 @@ def checkFilePermissions(dir_path): path = os.path.join(dir_path, file) if os.path.isfile(path): permission = oct(os.stat(path).st_mode) - if sys.version_info > (2, 7, 9): + if sys.version_info >= (3, 0): self.assertEqual(permission, '0o100644') else : self.assertEqual(permission, '0100644') From 0ee46852ec4f4d3029b0ac6a6d7a4d1d3388f2bf Mon Sep 17 00:00:00 2001 From: Artem Rys Date: Thu, 10 Nov 2022 22:52:33 +0100 Subject: [PATCH 19/32] Reuse splunklib.__version__ in handler.request --- splunklib/binding.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/splunklib/binding.py b/splunklib/binding.py index 17b5783c..370f076c 100644 --- a/splunklib/binding.py +++ b/splunklib/binding.py @@ -39,6 +39,7 @@ from io import BytesIO from xml.etree.ElementTree import XML +from splunklib import __version__ from splunklib import six from splunklib.six.moves import urllib @@ -1434,7 +1435,7 @@ def request(url, message, **kwargs): head = { "Content-Length": str(len(body)), "Host": host, - "User-Agent": "splunk-sdk-python/1.7.2", + "User-Agent": "splunk-sdk-python/%s" % __version__, "Accept": "*/*", "Connection": "Close", } # defaults From 31b5a92d3e653daecac6dad9c84690db82fb3370 Mon Sep 17 00:00:00 2001 From: Artem Rys Date: Thu, 10 Nov 2022 22:57:08 +0100 Subject: [PATCH 20/32] Add dependabot for github-actions --- .github/dependabot.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yaml diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 00000000..9df99942 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + target-branch: "master" + schedule: + interval: "weekly" From 0f376ca76c5e32ff5cd5bf0db14222abfc01e163 Mon Sep 17 00:00:00 2001 From: Artem Rys Date: Thu, 10 Nov 2022 22:57:27 +0100 Subject: [PATCH 21/32] Add Splunk 8.1 to the test suite --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 06278e29..560e8bc0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,6 +13,7 @@ jobs: - ubuntu-latest python: [ 2.7, 3.7 ] splunk-version: + - "8.1" - "8.2" - "latest" fail-fast: false From fe6b5bae53df6eb8598bbae0232736b100e9fbec Mon Sep 17 00:00:00 2001 From: akaila-splunk Date: Fri, 16 Dec 2022 18:15:31 +0530 Subject: [PATCH 22/32] update support for accelerated_fields of kvstore --- splunklib/client.py | 29 +++++++++++++++++++---------- tests/test_kvstore_conf.py | 13 +++++++++++++ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/splunklib/client.py b/splunklib/client.py index 564a40f6..457c3c33 100644 --- a/splunklib/client.py +++ b/splunklib/client.py @@ -3709,13 +3709,20 @@ class KVStoreCollections(Collection): def __init__(self, service): Collection.__init__(self, service, 'storage/collections/config', item=KVStoreCollection) - def create(self, name, indexes = {}, fields = {}, **kwargs): + def __getitem__(self, item): + res = Collection.__getitem__(self, item) + for k, v in res.content.items(): + if "accelerated_fields" in k: + res.content[k] = json.loads(v) + return res + + def create(self, name, accelerated_fields={}, fields={}, **kwargs): """Creates a KV Store Collection. :param name: name of collection to create :type name: ``string`` - :param indexes: dictionary of index definitions - :type indexes: ``dict`` + :param accelerated_fields: dictionary of accelerated_fields definitions + :type accelerated_fields: ``dict`` :param fields: dictionary of field definitions :type fields: ``dict`` :param kwargs: a dictionary of additional parameters specifying indexes and field definitions @@ -3723,10 +3730,10 @@ def create(self, name, indexes = {}, fields = {}, **kwargs): :return: Result of POST request """ - for k, v in six.iteritems(indexes): + for k, v in six.iteritems(accelerated_fields): if isinstance(v, dict): v = json.dumps(v) - kwargs['index.' + k] = v + kwargs['accelerated_fields.' + k] = v for k, v in six.iteritems(fields): kwargs['field.' + k] = v return self.post(name=name, **kwargs) @@ -3740,18 +3747,20 @@ def data(self): """ return KVStoreCollectionData(self) - def update_index(self, name, value): + def update_accelerated_field(self, name, value): """Changes the definition of a KV Store index. - :param name: name of index to change + :param name: name of accelerated_fields to change :type name: ``string`` - :param value: new index definition - :type value: ``dict`` or ``string`` + :param value: new accelerated_fields definition + :type value: ``dict`` :return: Result of POST request """ kwargs = {} - kwargs['index.' + name] = value if isinstance(value, six.string_types) else json.dumps(value) + if isinstance(value, dict): + value = json.dumps(value) + kwargs['accelerated_fields.' + name] = value return self.post(**kwargs) def update_field(self, name, value): diff --git a/tests/test_kvstore_conf.py b/tests/test_kvstore_conf.py index a2453728..20447df0 100755 --- a/tests/test_kvstore_conf.py +++ b/tests/test_kvstore_conf.py @@ -42,6 +42,12 @@ def test_create_delete_collection(self): self.confs['test'].delete() self.assertTrue(not 'test' in self.confs) + def test_create_fields(self): + self.confs.create('test', accelerated_fields={'ind1':{'a':1}}, fields={'a':'number1'}) + self.assertEqual(self.confs['test']['field.a'], 'number1') + self.assertEqual(self.confs['test']['accelerated_fields.ind1'], {"a": 1}) + self.confs['test'].delete() + def test_update_collection(self): self.confs.create('test') self.confs['test'].post(**{'accelerated_fields.ind1': '{"a": 1}', 'field.a': 'number'}) @@ -49,6 +55,13 @@ def test_update_collection(self): self.assertEqual(self.confs['test']['accelerated_fields.ind1'], '{"a": 1}') self.confs['test'].delete() + def test_update_accelerated_fields(self): + self.confs.create('test', accelerated_fields={'ind1':{'a':1}}) + self.assertEqual(self.confs['test']['accelerated_fields.ind1'], {'a': 1}) + # update accelerated_field value + self.confs['test'].update_accelerated_field('ind1', {'a': -1}) + self.assertEqual(self.confs['test']['accelerated_fields.ind1'], {'a': -1}) + self.confs['test'].delete() def test_update_fields(self): self.confs.create('test') From 721ede44c789e4a60f21dcf3e03110fe2dbcc373 Mon Sep 17 00:00:00 2001 From: akaila-splunk Date: Fri, 16 Dec 2022 19:03:17 +0530 Subject: [PATCH 23/32] Update test_kvstore_conf.py --- tests/test_kvstore_conf.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_kvstore_conf.py b/tests/test_kvstore_conf.py index 20447df0..d0d50336 100755 --- a/tests/test_kvstore_conf.py +++ b/tests/test_kvstore_conf.py @@ -15,6 +15,8 @@ # under the License. from __future__ import absolute_import + +import json from tests import testlib try: import unittest @@ -50,9 +52,10 @@ def test_create_fields(self): def test_update_collection(self): self.confs.create('test') - self.confs['test'].post(**{'accelerated_fields.ind1': '{"a": 1}', 'field.a': 'number'}) + val = {"a": 1} + self.confs['test'].post(**{'accelerated_fields.ind1': json.dumps(val), 'field.a': 'number'}) self.assertEqual(self.confs['test']['field.a'], 'number') - self.assertEqual(self.confs['test']['accelerated_fields.ind1'], '{"a": 1}') + self.assertEqual(self.confs['test']['accelerated_fields.ind1'], {"a": 1}) self.confs['test'].delete() def test_update_accelerated_fields(self): From 1afcdb01f6347d61ebb073d01f921c83acce51dd Mon Sep 17 00:00:00 2001 From: akaila-splunk Date: Mon, 19 Dec 2022 11:44:45 +0530 Subject: [PATCH 24/32] Update client.py --- splunklib/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/splunklib/client.py b/splunklib/client.py index 457c3c33..33156bb5 100644 --- a/splunklib/client.py +++ b/splunklib/client.py @@ -3748,7 +3748,7 @@ def data(self): return KVStoreCollectionData(self) def update_accelerated_field(self, name, value): - """Changes the definition of a KV Store index. + """Changes the definition of a KV Store accelerated_field. :param name: name of accelerated_fields to change :type name: ``string`` From 103cc954e4d6a418cd4424ccac3db0e9f70e7550 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Mon, 2 Jan 2023 14:03:22 +0530 Subject: [PATCH 25/32] Update README.md README updated on how to access service instance created using the server_uri and sessionkey metadata --- README.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 29b75704..34b753a7 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,67 @@ class GeneratorTest(GeneratingCommand): checkpoint_dir = inputs.metadata["checkpoint_dir"] ``` -#### Optional:Set up logging for splunklib +### Access service object in Custom Search Command & Modular Input apps + +#### Custom Search Commands +* The service object is created from the Splunkd URI and session key passed to the command invocation the search results info file. +* Generating Custom Search Command + ```python + def generate(self): + # other code + + # access service object that can be used to connect Splunk Service + service = self.service + # to get Splunk Service Info + info = service.info + ``` + * Eventing Custom Search Command + ```python + def transform(self, records): + # other code + + # access service object that can be used to connect Splunk Service + service = self.service + # to get Splunk Service Info + info = service.info + ``` + * Streaming Custom Search Command + ```python + def stream(self, records): + # other code + + # access service object that can be used to connect Splunk Service + service = self.service + # to get Splunk Service Info + info = service.info + ``` + * Reporting Custom Search Command + ```python + def reduce(self, records): + # other code + + # access service object that can be used to connect Splunk Service + service = self.service + # to get Splunk Service Info + info = service.info + ``` +* Service object can be accessed using `self.service` in `generate`/`transform`/`stream`/`reduce` methods depending on the Custom Search Command + +#### Modular Inputs app: +* The service object is created from the Splunkd URI and session key passed to the command invocation on the modular input stream respectively. +* It is available as soon as the :code:`Script.stream_events` method is called. +```python + def stream_events(self, inputs, ew): + # other code + + # access service object that can be used to connect Splunk Service + service = self.service + # to get Splunk Service Info + info = service.info +``` + + +### Optional:Set up logging for splunklib + The default level is WARNING, which means that only events of this level and above will be visible + To change a logging level we can call setup_logging() method and pass the logging level as an argument. + Optional: we can also pass log format and date format string as a method argument to modify default format From 6c8802fb421a3dd190690af57ace0319ee9a9614 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Mon, 2 Jan 2023 15:40:36 +0530 Subject: [PATCH 26/32] Release version updates --- CHANGELOG.md | 12 ++++++++++++ README.md | 4 ++-- splunklib/__init__.py | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ceda287..6a9069ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Splunk Enterprise SDK for Python Changelog +## Version 1.7.3 + +### Bug fixes +* [#493](https://github.com/splunk/splunk-sdk-python/pull/493) Fixed file permission for event_writer.py file [[issue#487](https://github.com/splunk/splunk-sdk-python/issues/487)] + +### Minor changes +* [#490](https://github.com/splunk/splunk-sdk-python/pull/490) Added ACL properties update feature +* [#500](https://github.com/splunk/splunk-sdk-python/pull/500) Replaced index_field with accelerated_field for kvstore +* [#495](https://github.com/splunk/splunk-sdk-python/pull/495) Added Splunk 8.1 in GitHub Actions Matrix +* [#485](https://github.com/splunk/splunk-sdk-python/pull/485) Added test case for cookie persistence +* []() README updates on accessing "service" instance in CSC and ModularInput apps + ## Version 1.7.2 ### Minor changes diff --git a/README.md b/README.md index 29b75704..efefe33b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # The Splunk Enterprise Software Development Kit for Python -#### Version 1.7.2 +#### Version 1.7.3 The Splunk Enterprise Software Development Kit (SDK) for Python contains library code designed to enable developers to build applications using the Splunk platform. @@ -30,7 +30,7 @@ Here's what you need to get going with the Splunk Enterprise SDK for Python. * Splunk Enterprise 9.0 or 8.2 - The Splunk Enterprise SDK for Python has been tested with Splunk Enterprise 9.0 and 8.2 + The Splunk Enterprise SDK for Python has been tested with Splunk Enterprise 9.0, 8.2 and 8.1 If you haven't already installed Splunk Enterprise, download it [here](http://www.splunk.com/download). For more information, see the Splunk Enterprise [_Installation Manual_](https://docs.splunk.com/Documentation/Splunk/latest/Installation). diff --git a/splunklib/__init__.py b/splunklib/__init__.py index 774cb757..31787bdc 100644 --- a/splunklib/__init__.py +++ b/splunklib/__init__.py @@ -31,5 +31,5 @@ def setup_logging(level, log_format=DEFAULT_LOG_FORMAT, date_format=DEFAULT_DATE format=log_format, datefmt=date_format) -__version_info__ = (1, 7, 2) +__version_info__ = (1, 7, 3) __version__ = ".".join(map(str, __version_info__)) From 054e249ed36c3dfa812c6ce7a84992e48495e348 Mon Sep 17 00:00:00 2001 From: akaila-splunk Date: Wed, 4 Jan 2023 18:04:27 +0530 Subject: [PATCH 27/32] Update test_kvstore_conf.py --- tests/test_kvstore_conf.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/test_kvstore_conf.py b/tests/test_kvstore_conf.py index d0d50336..98020342 100755 --- a/tests/test_kvstore_conf.py +++ b/tests/test_kvstore_conf.py @@ -93,17 +93,6 @@ def test_overlapping_collections(self): self.confs['test'].delete() self.confs['test'].delete() - """ - def test_create_accelerated_fields_fields(self): - self.confs.create('test', indexes={'foo': '{"foo": 1}', 'bar': {'bar': -1}}, **{'field.foo': 'string'}) - self.assertEqual(self.confs['test']['accelerated_fields.foo'], '{"foo": 1}') - self.assertEqual(self.confs['test']['field.foo'], 'string') - self.assertRaises(client.HTTPError, lambda: self.confs['test'].post(**{'accelerated_fields.foo': 'THIS IS INVALID'})) - self.assertEqual(self.confs['test']['accelerated_fields.foo'], '{"foo": 1}') - self.confs['test'].update_accelerated_fields('foo', '') - self.assertEqual(self.confs['test']['accelerated_fields.foo'], None) - """ - def tearDown(self): if ('test' in self.confs): self.confs['test'].delete() From 3fa049f0303eeb9fb7c36e9995ba9c55340f7ef6 Mon Sep 17 00:00:00 2001 From: akaila-splunk Date: Fri, 6 Jan 2023 17:45:12 +0530 Subject: [PATCH 28/32] update authentication token docs --- .env | 4 ++-- README.md | 4 ++-- scripts/templates/env.template | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.env b/.env index 59f7df83..c62498b0 100644 --- a/.env +++ b/.env @@ -11,6 +11,6 @@ scheme=https # Your version of Splunk (default: 6.2) version=9.0 # Bearer token for authentication -#bearerToken="" +#splunkToken="" # Session key for authentication -#sessionKey="" +#token="" diff --git a/README.md b/README.md index 29b75704..391157a3 100644 --- a/README.md +++ b/README.md @@ -111,9 +111,9 @@ here is an example of .env file: # Your version of Splunk Enterprise version=9.0 # Bearer token for authentication - #bearerToken= + #splunkToken= # Session key for authentication - #sessionKey= + #token= #### SDK examples diff --git a/scripts/templates/env.template b/scripts/templates/env.template index a45851b6..ac9ebe5c 100644 --- a/scripts/templates/env.template +++ b/scripts/templates/env.template @@ -11,6 +11,6 @@ scheme=$scheme # Your version of Splunk (default: 6.2) version=$version # Bearer token for authentication -#bearerToken= +#splunkToken= # Session key for authentication -#sessionKey= \ No newline at end of file +#token= \ No newline at end of file From f37c0d7730efd1b5f5eceec3d06e02abf3926929 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Mon, 9 Jan 2023 15:22:16 +0530 Subject: [PATCH 29/32] updated check for IPv6 addresses --- splunklib/binding.py | 3 ++- tests/test_binding.py | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/splunklib/binding.py b/splunklib/binding.py index 370f076c..85cb8d12 100644 --- a/splunklib/binding.py +++ b/splunklib/binding.py @@ -347,7 +347,8 @@ def _authority(scheme=DEFAULT_SCHEME, host=DEFAULT_HOST, port=DEFAULT_PORT): "http://splunk.utopia.net:471" """ - if ':' in host: + # check if host is an IPv6 address and not enclosed in [ ] + if ':' in host and not (host.startswith('[') and host.endswith(']')): # IPv6 addresses must be enclosed in [ ] in order to be well # formed. host = '[' + host + ']' diff --git a/tests/test_binding.py b/tests/test_binding.py index aa3a1391..2af294cf 100755 --- a/tests/test_binding.py +++ b/tests/test_binding.py @@ -190,6 +190,12 @@ def test_ipv6_host(self): host="2001:0db8:85a3:0000:0000:8a2e:0370:7334"), "https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8089") + def test_ipv6_host_enclosed(self): + self.assertEqual( + binding._authority( + host="[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"), + "https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8089") + def test_all_fields(self): self.assertEqual( binding._authority( From 83d753468a52e71ae46428dc5a370640e5e27521 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Tue, 10 Jan 2023 11:51:05 +0530 Subject: [PATCH 30/32] Update README.md --- README.md | 38 +++++--------------------------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 34b753a7..6b1b8772 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,8 @@ class GeneratorTest(GeneratingCommand): #### Custom Search Commands * The service object is created from the Splunkd URI and session key passed to the command invocation the search results info file. -* Generating Custom Search Command +* Service object can be accessed using `self.service` in `generate`/`transform`/`stream`/`reduce` methods depending on the Custom Search Command. +* For Generating Custom Search Command ```python def generate(self): # other code @@ -223,41 +224,12 @@ class GeneratorTest(GeneratingCommand): # to get Splunk Service Info info = service.info ``` - * Eventing Custom Search Command - ```python - def transform(self, records): - # other code - - # access service object that can be used to connect Splunk Service - service = self.service - # to get Splunk Service Info - info = service.info - ``` - * Streaming Custom Search Command - ```python - def stream(self, records): - # other code - - # access service object that can be used to connect Splunk Service - service = self.service - # to get Splunk Service Info - info = service.info - ``` - * Reporting Custom Search Command - ```python - def reduce(self, records): - # other code - - # access service object that can be used to connect Splunk Service - service = self.service - # to get Splunk Service Info - info = service.info - ``` -* Service object can be accessed using `self.service` in `generate`/`transform`/`stream`/`reduce` methods depending on the Custom Search Command + + #### Modular Inputs app: * The service object is created from the Splunkd URI and session key passed to the command invocation on the modular input stream respectively. -* It is available as soon as the :code:`Script.stream_events` method is called. +* It is available as soon as the `Script.stream_events` method is called. ```python def stream_events(self, inputs, ew): # other code From 869de77609c509df4cdb0c6389a61db0c14c8b90 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Mon, 30 Jan 2023 12:30:21 +0530 Subject: [PATCH 31/32] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a9069ce..ac494da9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,15 @@ ### Bug fixes * [#493](https://github.com/splunk/splunk-sdk-python/pull/493) Fixed file permission for event_writer.py file [[issue#487](https://github.com/splunk/splunk-sdk-python/issues/487)] +* [#502](https://github.com/splunk/splunk-sdk-python/pull/502) Updated check for IPv6 addresses +* [#500](https://github.com/splunk/splunk-sdk-python/pull/500) Replaced index_field with accelerated_field for kvstore ### Minor changes * [#490](https://github.com/splunk/splunk-sdk-python/pull/490) Added ACL properties update feature -* [#500](https://github.com/splunk/splunk-sdk-python/pull/500) Replaced index_field with accelerated_field for kvstore * [#495](https://github.com/splunk/splunk-sdk-python/pull/495) Added Splunk 8.1 in GitHub Actions Matrix * [#485](https://github.com/splunk/splunk-sdk-python/pull/485) Added test case for cookie persistence -* []() README updates on accessing "service" instance in CSC and ModularInput apps +* [#503](https://github.com/splunk/splunk-sdk-python/pull/503) README updates on accessing "service" instance in CSC and ModularInput apps +* [#504](https://github.com/splunk/splunk-sdk-python/pull/504) Updated authentication token names in docs to reduce confusion ## Version 1.7.2 From ba791fb1f7c05b18df8b64427ed840966feb8f19 Mon Sep 17 00:00:00 2001 From: Abhi Shah Date: Wed, 1 Feb 2023 11:52:32 +0530 Subject: [PATCH 32/32] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac494da9..f7d56752 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,8 @@ ### Bug fixes * [#493](https://github.com/splunk/splunk-sdk-python/pull/493) Fixed file permission for event_writer.py file [[issue#487](https://github.com/splunk/splunk-sdk-python/issues/487)] +* [#500](https://github.com/splunk/splunk-sdk-python/pull/500) Replaced index_field with accelerated_field for kvstore [[issue#497](https://github.com/splunk/splunk-sdk-python/issues/497)] * [#502](https://github.com/splunk/splunk-sdk-python/pull/502) Updated check for IPv6 addresses -* [#500](https://github.com/splunk/splunk-sdk-python/pull/500) Replaced index_field with accelerated_field for kvstore ### Minor changes * [#490](https://github.com/splunk/splunk-sdk-python/pull/490) Added ACL properties update feature @@ -13,6 +13,7 @@ * [#485](https://github.com/splunk/splunk-sdk-python/pull/485) Added test case for cookie persistence * [#503](https://github.com/splunk/splunk-sdk-python/pull/503) README updates on accessing "service" instance in CSC and ModularInput apps * [#504](https://github.com/splunk/splunk-sdk-python/pull/504) Updated authentication token names in docs to reduce confusion +* [#494](https://github.com/splunk/splunk-sdk-python/pull/494) Reuse splunklib.__version__ in handler.request ## Version 1.7.2