Skip to content
This repository has been archived by the owner on Apr 23, 2023. It is now read-only.

Commit

Permalink
Fixed splunkbase check_python_sdk_version see issue #45
Browse files Browse the repository at this point in the history
  • Loading branch information
jorritfolmer committed Oct 21, 2022
1 parent fa44e43 commit 9477692
Show file tree
Hide file tree
Showing 138 changed files with 7,375 additions and 2,419 deletions.
4 changes: 2 additions & 2 deletions app.manifest
Expand Up @@ -5,7 +5,7 @@
"id": {
"group": null,
"name": "TA-dmarc",
"version": "4.1.0"
"version": "4.1.1"
},
"author": [
{
Expand Down Expand Up @@ -50,4 +50,4 @@
"Enterprise": "*"
}
}
}
}
19 changes: 17 additions & 2 deletions bin/ta_dmarc/aob_py2/solnlib/packages/splunklib/__init__.py
Expand Up @@ -15,6 +15,21 @@
"""Python library for Splunk."""

from __future__ import absolute_import
from .six.moves import map
__version_info__ = (1, 6, 6)
from splunklib.six.moves import map
import logging

DEFAULT_LOG_FORMAT = '%(asctime)s, Level=%(levelname)s, Pid=%(process)s, Logger=%(name)s, File=%(filename)s, ' \
'Line=%(lineno)s, %(message)s'
DEFAULT_DATE_FORMAT = '%Y-%m-%d %H:%M:%S %Z'


# To set the logging level of splunklib
# ex. To enable debug logs, call this method with parameter 'logging.DEBUG'
# default logging level is set to 'WARNING'
def setup_logging(level, log_format=DEFAULT_LOG_FORMAT, date_format=DEFAULT_DATE_FORMAT):
logging.basicConfig(level=level,
format=log_format,
datefmt=date_format)

__version_info__ = (1, 6, 20)
__version__ = ".".join(map(str, __version_info__))
130 changes: 96 additions & 34 deletions bin/ta_dmarc/aob_py2/solnlib/packages/splunklib/binding.py
Expand Up @@ -31,16 +31,16 @@
import socket
import ssl
import sys
import time
from base64 import b64encode
from contextlib import contextmanager
from datetime import datetime
from functools import wraps
from io import BytesIO
from xml.etree.ElementTree import XML

from . import six
from .six import StringIO
from .six.moves import urllib
from splunklib import six
from splunklib.six.moves import urllib

from .data import record

Expand All @@ -49,6 +49,7 @@
except ImportError as e:
from xml.parsers.expat import ExpatError as ParseError

logger = logging.getLogger(__name__)

__all__ = [
"AuthenticationError",
Expand All @@ -70,7 +71,7 @@ def new_f(*args, **kwargs):
start_time = datetime.now()
val = f(*args, **kwargs)
end_time = datetime.now()
logging.debug("Operation took %s", end_time-start_time)
logger.debug("Operation took %s", end_time-start_time)
return val
return new_f

Expand All @@ -80,6 +81,7 @@ def _parse_cookies(cookie_str, dictionary):
then updates the the dictionary with any key-value pairs found.
**Example**::
dictionary = {}
_parse_cookies('my=value', dictionary)
# Now the following is True
Expand Down Expand Up @@ -295,8 +297,7 @@ def wrapper(self, *args, **kwargs):
with _handle_auth_error("Autologin failed."):
self.login()
with _handle_auth_error(
"Autologin succeeded, but there was an auth error on "
"next request. Something is very wrong."):
"Authentication Failed! If session token is used, it seems to have been expired."):
return request_fun(self, *args, **kwargs)
elif he.status == 401 and not self.autologin:
raise AuthenticationError(
Expand Down Expand Up @@ -449,8 +450,16 @@ class Context(object):
:type username: ``string``
:param password: The password for the Splunk account.
:type password: ``string``
:param splunkToken: Splunk authentication token
:type splunkToken: ``string``
:param headers: List of extra HTTP headers to send (optional).
:type headers: ``list`` of 2-tuples.
:param retires: Number of retries for each HTTP connection (optional, the default is 0).
NOTE THAT THIS MAY INCREASE THE NUMBER OF ROUND TRIP CONNECTIONS TO THE SPLUNK SERVER AND BLOCK THE
CURRENT THREAD WHILE RETRYING.
:type retries: ``int``
:param retryDelay: How long to wait between connection attempts if `retries` > 0 (optional, defaults to 10s).
:type retryDelay: ``int`` (in seconds)
:param handler: The HTTP request handler (optional).
:returns: A ``Context`` instance.
Expand All @@ -468,7 +477,8 @@ class Context(object):
"""
def __init__(self, handler=None, **kwargs):
self.http = HttpLib(handler, kwargs.get("verify", False), key_file=kwargs.get("key_file"),
cert_file=kwargs.get("cert_file")) # Default to False for backward compat
cert_file=kwargs.get("cert_file"), context=kwargs.get("context"), # Default to False for backward compat
retries=kwargs.get("retries", 0), retryDelay=kwargs.get("retryDelay", 10))
self.token = kwargs.get("token", _NoAuthenticationToken)
if self.token is None: # In case someone explicitly passes token=None
self.token = _NoAuthenticationToken
Expand All @@ -480,6 +490,7 @@ def __init__(self, handler=None, **kwargs):
self.username = kwargs.get("username", "")
self.password = kwargs.get("password", "")
self.basic = kwargs.get("basic", False)
self.bearerToken = kwargs.get("splunkToken", "")
self.autologin = kwargs.get("autologin", False)
self.additional_headers = kwargs.get("headers", [])

Expand All @@ -496,13 +507,13 @@ def get_cookies(self):
return self.http._cookies

def has_cookies(self):
"""Returns true if the ``HttpLib`` member of this instance has at least
one cookie stored.
"""Returns true if the ``HttpLib`` member of this instance has auth token stored.
:return: ``True`` if there is at least one cookie, else ``False``
:return: ``True`` if there is auth token present, else ``False``
:rtype: ``bool``
"""
return len(self.get_cookies()) > 0
auth_token_key = "splunkd_"
return any(auth_token_key in key for key in self.get_cookies().keys())

# Shared per-context request headers
@property
Expand All @@ -520,6 +531,9 @@ def _auth_headers(self):
elif self.basic and (self.username and self.password):
token = 'Basic %s' % b64encode(("%s:%s" % (self.username, self.password)).encode('utf-8')).decode('ascii')
return [("Authorization", token)]
elif self.bearerToken:
token = 'Bearer %s' % self.bearerToken
return [("Authorization", token)]
elif self.token is _NoAuthenticationToken:
return []
else:
Expand Down Expand Up @@ -611,7 +625,7 @@ def delete(self, path_segment, owner=None, app=None, sharing=None, **query):
"""
path = self.authority + self._abspath(path_segment, owner=owner,
app=app, sharing=sharing)
logging.debug("DELETE request to %s (body: %s)", path, repr(query))
logger.debug("DELETE request to %s (body: %s)", path, repr(query))
response = self.http.delete(path, self._auth_headers, **query)
return response

Expand Down Expand Up @@ -674,7 +688,7 @@ def get(self, path_segment, owner=None, app=None, headers=None, sharing=None, **

path = self.authority + self._abspath(path_segment, owner=owner,
app=app, sharing=sharing)
logging.debug("GET request to %s (body: %s)", path, repr(query))
logger.debug("GET request to %s (body: %s)", path, repr(query))
all_headers = headers + self.additional_headers + self._auth_headers
response = self.http.get(path, all_headers, **query)
return response
Expand Down Expand Up @@ -717,7 +731,12 @@ def post(self, path_segment, owner=None, app=None, sharing=None, headers=None, *
:type headers: ``list`` of 2-tuples.
:param query: All other keyword arguments, which are used as query
parameters.
:type query: ``string``
:param body: Parameters to be used in the post body. If specified,
any parameters in the query will be applied to the URL instead of
the body. If a dict is supplied, the key-value pairs will be form
encoded. If a string is supplied, the body will be passed through
in the request unchanged.
:type body: ``dict`` or ``str``
:return: The response from the server.
:rtype: ``dict`` with keys ``body``, ``headers``, ``reason``,
and ``status``
Expand Down Expand Up @@ -747,14 +766,20 @@ def post(self, path_segment, owner=None, app=None, sharing=None, headers=None, *
headers = []

path = self.authority + self._abspath(path_segment, owner=owner, app=app, sharing=sharing)
logging.debug("POST request to %s (body: %s)", path, repr(query))

# To avoid writing sensitive data in debug logs
endpoint_having_sensitive_data = ["/storage/passwords"]
if any(endpoint in path for endpoint in endpoint_having_sensitive_data):
logger.debug("POST request to %s ", path)
else:
logger.debug("POST request to %s (body: %s)", path, repr(query))
all_headers = headers + self.additional_headers + self._auth_headers
response = self.http.post(path, all_headers, **query)
return response

@_authentication
@_log_duration
def request(self, path_segment, method="GET", headers=None, body="",
def request(self, path_segment, method="GET", headers=None, body={},
owner=None, app=None, sharing=None):
"""Issues an arbitrary HTTP request to the REST path segment.
Expand Down Expand Up @@ -783,9 +808,6 @@ def request(self, path_segment, method="GET", headers=None, body="",
:type app: ``string``
:param sharing: The sharing mode of the namespace (optional).
:type sharing: ``string``
:param query: All other keyword arguments, which are used as query
parameters.
:type query: ``string``
:return: The response from the server.
:rtype: ``dict`` with keys ``body``, ``headers``, ``reason``,
and ``status``
Expand Down Expand Up @@ -814,13 +836,28 @@ def request(self, path_segment, method="GET", headers=None, body="",
path = self.authority \
+ self._abspath(path_segment, owner=owner,
app=app, sharing=sharing)

all_headers = headers + self.additional_headers + self._auth_headers
logging.debug("%s request to %s (headers: %s, body: %s)",
logger.debug("%s request to %s (headers: %s, body: %s)",
method, path, str(all_headers), repr(body))
response = self.http.request(path,
{'method': method,
'headers': all_headers,
'body': body})

if body:
body = _encode(**body)

if method == "GET":
path = path + UrlEncoded('?' + body, skip_encode=True)
message = {'method': method,
'headers': all_headers}
else:
message = {'method': method,
'headers': all_headers,
'body': body}
else:
message = {'method': method,
'headers': all_headers}

response = self.http.request(path, message)

return response

def login(self):
Expand Down Expand Up @@ -862,6 +899,10 @@ def login(self):
# as credentials were passed in.
return

if self.bearerToken:
# Bearer auth mode requested, so this method is a nop as long
# as authentication token was passed in.
return
# Only try to get a token and updated cookie if username & password are specified
try:
response = self.http.post(
Expand Down Expand Up @@ -1004,7 +1045,7 @@ class HTTPError(Exception):
def __init__(self, response, _message=None):
status = response.status
reason = response.reason
body = (response.body.read()).decode()
body = response.body.read()
try:
detail = XML(body).findtext("./messages/msg")
except ParseError as err:
Expand Down Expand Up @@ -1054,7 +1095,7 @@ def __init__(self, message, cause):
#

# Encode the given kwargs as a query string. This wrapper will also _encode
# a list value as a sequence of assignemnts to the corresponding arg name,
# a list value as a sequence of assignments to the corresponding arg name,
# for example an argument such as 'foo=[1,2,3]' will be encoded as
# 'foo=1&foo=2&foo=3'.
def _encode(**kwargs):
Expand Down Expand Up @@ -1121,12 +1162,14 @@ class HttpLib(object):
If using the default handler, SSL verification can be disabled by passing verify=False.
"""
def __init__(self, custom_handler=None, verify=False, key_file=None, cert_file=None):
def __init__(self, custom_handler=None, verify=False, key_file=None, cert_file=None, context=None, retries=0, retryDelay=10):
if custom_handler is None:
self.handler = handler(verify=verify, key_file=key_file, cert_file=cert_file)
self.handler = handler(verify=verify, key_file=key_file, cert_file=cert_file, context=context)
else:
self.handler = custom_handler
self._cookies = {}
self.retries = retries
self.retryDelay = retryDelay

def delete(self, url, headers=None, **kwargs):
"""Sends a DELETE request to a URL.
Expand Down Expand Up @@ -1212,6 +1255,8 @@ def post(self, url, headers=None, **kwargs):
headers.append(("Content-Type", "application/x-www-form-urlencoded"))

body = kwargs.pop('body')
if isinstance(body, dict):
body = _encode(**body).encode('utf-8')
if len(kwargs) > 0:
url = url + UrlEncoded('?' + _encode(**kwargs), skip_encode=True)
else:
Expand All @@ -1238,7 +1283,16 @@ def request(self, url, message, **kwargs):
its structure).
:rtype: ``dict``
"""
response = self.handler(url, message, **kwargs)
while True:
try:
response = self.handler(url, message, **kwargs)
break
except Exception:
if self.retries <= 0:
raise
else:
time.sleep(self.retryDelay)
self.retries -= 1
response = record(response)
if 400 <= response.status:
raise HTTPError(response)
Expand Down Expand Up @@ -1274,7 +1328,10 @@ def __init__(self, response, connection=None):
self._buffer = b''

def __str__(self):
return self.read()
if six.PY2:
return self.read()
else:
return str(self.read(), 'UTF-8')

@property
def empty(self):
Expand Down Expand Up @@ -1333,7 +1390,7 @@ def readinto(self, byte_array):
return bytes_read


def handler(key_file=None, cert_file=None, timeout=None, verify=False):
def handler(key_file=None, cert_file=None, timeout=None, verify=False, context=None):
"""This class returns an instance of the default HTTP request handler using
the values you provide.
Expand All @@ -1345,6 +1402,8 @@ def handler(key_file=None, cert_file=None, timeout=None, verify=False):
:type timeout: ``integer`` or "None"
:param `verify`: Set to False to disable SSL verification on https connections.
:type verify: ``Boolean``
:param `context`: The SSLContext that can is used with the HTTPSConnection when verify=True is enabled and context is specified
:type context: ``SSLContext`
"""

def connect(scheme, host, port):
Expand All @@ -1356,9 +1415,12 @@ def connect(scheme, host, port):
if key_file is not None: kwargs['key_file'] = key_file
if cert_file is not None: kwargs['cert_file'] = cert_file

# If running Python 2.7.9+, disable SSL certificate validation
if (sys.version_info >= (2,7,9) and key_file is None and cert_file is None) and not verify:
if not verify:
kwargs['context'] = ssl._create_unverified_context()
elif context:
# verify is True in elif branch and context is not None
kwargs['context'] = context

return six.moves.http_client.HTTPSConnection(host, port, **kwargs)
raise ValueError("unsupported scheme: %s" % scheme)

Expand All @@ -1368,7 +1430,7 @@ def request(url, message, **kwargs):
head = {
"Content-Length": str(len(body)),
"Host": host,
"User-Agent": "splunk-sdk-python/1.6.6",
"User-Agent": "splunk-sdk-python/1.6.20",
"Accept": "*/*",
"Connection": "Close",
} # defaults
Expand Down

0 comments on commit 9477692

Please sign in to comment.