Skip to content

Commit

Permalink
Merge pull request #1 from oasis-open/master
Browse files Browse the repository at this point in the history
update
  • Loading branch information
zrush-mitre authored Nov 25, 2020
2 parents 0a897ae + f7daa83 commit 9aac9aa
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 87 deletions.
1 change: 0 additions & 1 deletion .isort.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
[settings]
not_skip = __init__.py
known_third_party=
pytest,
pytz,
Expand Down
3 changes: 3 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
sha: v0.9.2
hooks:
Expand All @@ -10,3 +11,5 @@
sha: b57843b0b874df1d16eb0bef00b868792cb245c2
hooks:
- id: python-import-sorter
args:
- --diff
8 changes: 3 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
language: python
cache: pip
dist: xenial
dist: bionic
python:
- "2.7"
- "3.4"
- "3.5"
- "3.6"
- "3.7"
Expand All @@ -12,9 +10,9 @@ install:
- pip install -U pip setuptools
- pip install tox-travis
- pip install codecov
- if [[ $TRAVIS_PYTHON_VERSION != 3.4 ]]; then pip install pre-commit; fi
- pip install pre-commit
script:
- tox
- if [[ $TRAVIS_PYTHON_VERSION != 3.4 ]]; then pre-commit run --all-files; fi
- pre-commit run --all-files
after_success:
- codecov
20 changes: 20 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
Version 2.2.2
2020-09-10
- #85 Provide more details when a Server Response status code is 406. (v20, v21)
- #84 Fallback "Range" HTTP Header for pagination requests. (@teizenman) (v20)
- #81 More flexibility for the Status Endpoint validation. It no longer fails for optional successes, pendings, failures lists (@maybe-sybr) (v20, v21)

Version 2.2.1
2020-07-17
- #78 Prevent KeyError when TAXII Server Response does not include 'Content-Range' Header (v20)

Version 2.2.0
2020-07-02
- #76 drop python versions older than 3.5
- #75 fixes construction of Range Header for paginated requests RFC 7233. (@dougle)

Version 2.1.0
2020-06-09
- #68 Support for user-specified authentication including token-based authentication.
- #71 Don't log a warning when the total number of available objects is less than the per request size limit.

Version 2.0.0
2020-04-01
Base release of the TAXII Client for 2.1. Unsupported features:
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@
# built documents.
#
# The short X.Y version.
version = '2.0.0'
version = '2.2.2'
# The full version, including alpha/beta/rc tags.
release = '2.0.0'
release = '2.2.2'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 2.0.0
current_version = 2.2.2
commit = True
tag = True

Expand Down
4 changes: 0 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,7 @@ def get_long_description():
'Intended Audience :: Developers',
'Topic :: Security',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
Expand Down
15 changes: 14 additions & 1 deletion taxii2client/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
"""Python TAXII 2 Client"""

# flake8: noqa
# isort:skip_file

DEFAULT_USER_AGENT = "taxii2-client/2.0.0"
import logging

# Console Handler for taxii2client messages
ch = logging.StreamHandler()
ch.setFormatter(logging.Formatter("[%(name)s] [%(levelname)-8s] [%(asctime)s] %(message)s"))

# Module-level logger
log = logging.getLogger(__name__)
log.propagate = False
log.addHandler(ch)


DEFAULT_USER_AGENT = "taxii2-client/2.2.2"
MEDIA_TYPE_STIX_V20 = "application/vnd.oasis.stix+json; version=2.0"
MEDIA_TYPE_TAXII_V20 = "application/vnd.oasis.taxii+json; version=2.0"
MEDIA_TYPE_TAXII_V21 = "application/taxii+json; version=2.1"
Expand Down
54 changes: 44 additions & 10 deletions taxii2client/common.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import datetime
import logging
import re

import pytz
Expand All @@ -12,6 +13,9 @@
InvalidArgumentsError, InvalidJSONError, TAXIIServiceException
)

# Module-level logger
log = logging.getLogger(__name__)


def _format_datetime(dttm):
"""Convert a datetime object into a valid STIX timestamp string.
Expand Down Expand Up @@ -131,10 +135,22 @@ def _grab_total_items(resp):
try:
results = re.match(r"^items (\d+)-(\d+)/(\d+)$", resp.headers["Content-Range"])
return int(results.group(2)) - int(results.group(1)) + 1, int(results.group(3))
except ValueError as e:
except (ValueError, IndexError) as e:
six.raise_from(InvalidJSONError(
"Invalid Content-Range was received from " + resp.request.url
), e)
except KeyError:
log.warning("TAXII Server Response did not include 'Content-Range' header - results could be incomplete")
return 0, 0


class TokenAuth(requests.auth.AuthBase):
def __init__(self, key):
self.key = key

def __call__(self, r):
r.headers['Authorization'] = 'Token {}'.format(self.key)
return r


class _TAXIIEndpoint(object):
Expand All @@ -145,7 +161,7 @@ class _TAXIIEndpoint(object):
"""
def __init__(self, url, conn=None, user=None, password=None, verify=True,
proxies=None, version="2.0"):
proxies=None, version="2.0", auth=None):
"""Create a TAXII endpoint.
Args:
Expand All @@ -158,13 +174,13 @@ def __init__(self, url, conn=None, user=None, password=None, verify=True,
version (str): The spec version this connection is meant to follow.
"""
if conn and (user or password):
raise InvalidArgumentsError("A connection and user/password may"
" not both be provided.")
if (conn and ((user or password) or auth)) or ((user or password) and auth):
raise InvalidArgumentsError("Only one of a connection, username/password, or auth object may"
" be provided.")
elif conn:
self._conn = conn
else:
self._conn = _HTTPConnection(user, password, verify, proxies, version=version)
self._conn = _HTTPConnection(user, password, verify, proxies, version=version, auth=auth)

# Add trailing slash to TAXII endpoint if missing
# https://github.com/oasis-open/cti-taxii-client/issues/50
Expand Down Expand Up @@ -201,7 +217,7 @@ class _HTTPConnection(object):
"""

def __init__(self, user=None, password=None, verify=True, proxies=None,
user_agent=DEFAULT_USER_AGENT, version="2.0"):
user_agent=DEFAULT_USER_AGENT, version="2.0", auth=None):
"""Create a connection session.
Args:
Expand All @@ -219,8 +235,12 @@ def __init__(self, user=None, password=None, verify=True, proxies=None,
self.session.verify = verify
# enforce that we always have a connection-default user agent.
self.user_agent = user_agent or DEFAULT_USER_AGENT

if user and password:
self.session.auth = requests.auth.HTTPBasicAuth(user, password)
elif auth:
self.session.auth = auth

if proxies:
self.session.proxies.update(proxies)
self.version = version
Expand Down Expand Up @@ -275,12 +295,26 @@ def get(self, url, headers=None, params=None):

resp = self.session.get(url, headers=merged_headers, params=params)

resp.raise_for_status()
try:
resp.raise_for_status()
except requests.exceptions.HTTPError as e:
if resp.status_code == 406:
# Provide more details about this error since its usually an import problem.
# Import the correct version of the TAXII Client.
logging.error(
"Server Response: 406 Client Error "
"If you are trying to contact a TAXII 2.0 Server use 'from taxii2client.v20 import X'. "
"If you are trying to contact a TAXII 2.1 Server use 'from taxii2client.v21 import X'"
)
raise e

content_type = resp.headers["Content-Type"]

if not self.valid_content_type(content_type=content_type, accept=accept):
msg = "Unexpected Response. Got Content-Type: '{}' for Accept: '{}'"
msg = (
"Unexpected Response. Got Content-Type: '{}' for Accept: '{}'\n"
"If you are trying to contact a TAXII 2.0 Server use 'from taxii2client.v20 import X'\n"
"If you are trying to contact a TAXII 2.1 Server use 'from taxii2client.v21 import X'"
)
raise TAXIIServiceException(msg.format(content_type, accept))

if "Range" in merged_headers and self.version == "2.0":
Expand Down
Loading

0 comments on commit 9aac9aa

Please sign in to comment.