diff --git a/README.md b/README.md new file mode 100644 index 0000000..becdf4c --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# Intuit OAuth Python Client + +A Python package to manage OAuth2 authentication with Intuits API. + +## Rationale + +This package is a fork from the official Intuit OAuth [repository](https://github.com/intuit/oauth-pythonclient). + +It was forked after 2 years of inactivity and lack of maintenance on the original project. +I wanted to use it and was frustrated with how it was designed; it made it particularly costly on unit tests. +PRs that would have fixed this were closed with no action or response by the maintainers. + +The lack of engagement from Intuit is telling and it's better to have an engaged community-driven fork, than a +poorly maintained official project. + +## Installation + +`pip install intuit-oauth-client` + +## Usage + +See original [repository](https://github.com/intuit/oauth-pythonclient) + +## Changes + +Notable changes from the official repository are: + +1. Remove HTTP requests on AuthClient __init__ constructor +2. Lazy load the discovery doc dictionary and all corresponding attributes that were being set on __init__ +3. Remove subclass on `requests.Session`. A separate session can be passed on __init__ or one is created +4. Update project to `poetry` with a pyproject.toml file +5. Improve unit tests fixtures. Tests run much faster now issuing far fewer actual HTTP requests +6. Responsive unpaid maintainer + +## Contributions + +Feel free to submit an issue or pull request, I promise to respond + +## License + +This library is provided under Apache 2.0 which is found [here](https://github.com/SunPowered/intuit-oauth-pythonclient/blob/master/LICENSE) + + diff --git a/README.rst b/README.rst deleted file mode 100644 index 6d1b879..0000000 --- a/README.rst +++ /dev/null @@ -1,56 +0,0 @@ -.. image:: views/SDK.png - :target: https://help.developer.intuit.com/s/samplefeedback?cid=1110&repoName=oauth-pythonclient - -Intuit's OAuth2 and OpenID Python Client -======================================== - -|build| |coverage| |docs| - -.. |build| image:: https://travis-ci.com/intuit/oauth-pythonclient.svg?branch=master - :target: https://travis-ci.com/intuit/oauth-pythonclient - -.. |coverage| image:: https://coveralls.io/repos/github/intuit/oauth-pythonclient/badge.svg?branch=master - :target: https://coveralls.io/github/intuit/oauth-pythonclient?branch=master - -.. |docs| image:: https://readthedocs.org/projects/oauth-pythonclient/badge/?version=latest - :target: https://oauth-pythonclient.readthedocs.io/en/latest/?badge=latest - :alt: Documentation Status - -This client library is meant to work with Intuit's OAuth and OpenID implementation. The `AuthClient` object response can be used for User Info API, Accounting API and Payments API. This library supports: - -- Generating Authorization URL -- Getting OAuth2 Bearer Token -- Getting User Info -- Validating OpenID token -- Refreshing OAuth2 Token -- Revoking OAuth2 Token -- Migrating tokens from OAuth1.0 to OAuth2 - -Install -------- - -Using `pip `_: :: - - $ pip install intuit-oauth - -Documentation -------------- - -Usage and Reference Documentation can be found at `oauth-pythonclient.readthedocs.io `_ - -Sample App ----------- - -Sample app for this library can be found at `IntuitDeveloper GitHub Org `_ - -Issues and Contributions ------------------------- - -Please open an `issue `_ on GitHub if you have a problem, suggestion, or other comment. - -Pull requests are welcome and encouraged! Any contributions should include new or updated unit tests as necessary to maintain thorough test coverage. - -License -------- - -This library is provided under Apache 2.0 which is found `here `__ diff --git a/intuitlib/__init__.py b/intuitlib/__init__.py index 0865c74..f7d5a7b 100644 --- a/intuitlib/__init__.py +++ b/intuitlib/__init__.py @@ -12,10 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -try: - import pkg_resources - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - __path__ = pkgutil.extend_path(__path__, __name__) diff --git a/intuitlib/client.py b/intuitlib/client.py index fc3ae6a..07f37ee 100644 --- a/intuitlib/client.py +++ b/intuitlib/client.py @@ -12,11 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import absolute_import - import json import requests -from future.moves.urllib.parse import urlencode +from urllib.parse import urlencode from intuitlib.utils import ( get_discovery_doc, @@ -26,11 +24,11 @@ send_request, ) -class AuthClient(requests.Session): +class AuthClient: """Handles OAuth 2.0 and OpenID Connect flows to get access to User Info API, Accounting APIs and Payments APIs """ - def __init__(self, client_id, client_secret, redirect_uri, environment, state_token=None, access_token=None, refresh_token=None, id_token=None, realm_id=None): + def __init__(self, client_id, client_secret, redirect_uri, environment, state_token=None, access_token=None, refresh_token=None, id_token=None, realm_id=None, session=None): """Constructor for AuthClient :param client_id: Client ID found in developer account Keys tab @@ -42,10 +40,9 @@ def __init__(self, client_id, client_secret, redirect_uri, environment, state_to :param refresh_token: Refresh Token for refresh or revoke functionality, defaults to None :param id_token: ID Token for OpenID flow, defaults to None :param realm_id: QBO Realm/Company ID, defaults to None + :param session: Optional requests.Session object to use """ - super(AuthClient, self).__init__() - self.client_id = client_id self.client_secret = client_secret self.redirect_uri = redirect_uri @@ -53,13 +50,13 @@ def __init__(self, client_id, client_secret, redirect_uri, environment, state_to self.state_token = state_token # Discovery doc contains endpoints based on environment specified - discovery_doc = get_discovery_doc(self.environment, session=self) - self.auth_endpoint = discovery_doc['authorization_endpoint'] - self.token_endpoint = discovery_doc['token_endpoint'] - self.revoke_endpoint = discovery_doc['revocation_endpoint'] - self.issuer_uri = discovery_doc['issuer'] - self.jwks_uri = discovery_doc['jwks_uri'] - self.user_info_url = discovery_doc['userinfo_endpoint'] + self._discovery_doc = None + self._auth_endpoint = None + self._token_endpoint = None + self._revoke_endpoint = None + self._issuer_uri = None + self._jwks_uri = None + self._user_info_url = None # response values self.realm_id = realm_id @@ -68,7 +65,62 @@ def __init__(self, client_id, client_secret, redirect_uri, environment, state_to self.refresh_token = refresh_token self.x_refresh_token_expires_in = None self.id_token = id_token + + # Session + if session is not None and not isinstance(session, requests.Session): + raise TypeError(f"Bad parameter type for session. Expected requests.Session, got {type(session)}") + self.session = session or requests.Session() + + @property + def discovery_doc(self): + """Cache the discovery doc dictionary returned from get_discovery_doc""" + if self._discovery_doc is None: + self._discovery_doc = get_discovery_doc(self.environment, session=self) + return self._discovery_doc + + @property + def auth_endpoint(self): + """Cache authorization_endpoint from the discovery doc""" + if self._auth_endpoint is None: + self._auth_endpoint = self.discovery_doc['authorization_endpoint'] + return self._auth_endpoint + + @property + def token_endpoint(self): + """Cache token_endpoint from the discovery doc""" + if self._token_endpoint is None: + self._token_endpoint = self.discovery_doc['token_endpoint'] + return self._token_endpoint + + @property + def revoke_endpoint(self): + """Cache revocation_endpoint from the discovery doc""" + if self._revoke_endpoint is None: + self._revoke_endpoint = self.discovery_doc['revocation_endpoint'] + return self._revoke_endpoint + + @property + def issuer_uri(self): + """Cache issuer from the discovery doc""" + if self._issuer_uri is None: + self._issuer_uri = self.discovery_doc['issuer'] + return self._issuer_uri + + @property + def jwks_uri(self): + """Cache jwks_uri from the discovery doc""" + if self._jwks_uri is None: + self._jwks_uri = self.discovery_doc['jwks_uri'] + return self._jwks_uri + + @property + def user_info_url(self): + """Cache userinfo_endpoint from the discovery doc""" + if self._user_info_url is None: + self._user_info_url = self.discovery_doc['userinfo_endpoint'] + return self._user_info_url + def setAuthorizeURLs(self, urlObject): """Set authorization url using custom values passed in the data dict :param **data: data dict for custom authorizationURLS diff --git a/intuitlib/migration.py b/intuitlib/migration.py index da142cb..abe0edd 100644 --- a/intuitlib/migration.py +++ b/intuitlib/migration.py @@ -14,8 +14,6 @@ """This module helps in migrating OAuth 1.0a tokens to OAuth 2.0 """ -from __future__ import absolute_import - import json from requests_oauthlib import OAuth1 diff --git a/intuitlib/utils.py b/intuitlib/utils.py index f3fb700..ef564e0 100644 --- a/intuitlib/utils.py +++ b/intuitlib/utils.py @@ -14,8 +14,6 @@ """This module contains utility methods used by this library """ -from __future__ import absolute_import - import json from base64 import b64encode, b64decode, urlsafe_b64decode from datetime import datetime @@ -24,7 +22,6 @@ from jose import jwk import requests from requests.sessions import Session -import six from requests_oauthlib import OAuth1 @@ -109,9 +106,7 @@ def get_auth_header(client_id, client_secret): :return: Authorization header """ - auth_header = '{0}:{1}'.format(client_id, client_secret) - if six.PY3: - auth_header = auth_header.encode('utf-8') + auth_header = '{0}:{1}'.format(client_id, client_secret).encode('utf-8') return ' '.join(['Basic', b64encode(auth_header).decode('utf-8')]) def scopes_to_string(scopes): diff --git a/intuitlib/version.py b/intuitlib/version.py index d2811fe..025a95d 100644 --- a/intuitlib/version.py +++ b/intuitlib/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = '1.2.4' +__version__ = '2.0.1' diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..83348f9 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,516 @@ +[[package]] +name = "attrs" +version = "22.1.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.extras] +dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] +docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] +tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] +tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] + +[[package]] +name = "certifi" +version = "2022.9.24" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "charset-normalizer" +version = "2.1.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.6.0" + +[package.extras] +unicode-backport = ["unicodedata2"] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" + +[[package]] +name = "coverage" +version = "6.5.0" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "ecdsa" +version = "0.18.0" +description = "ECDSA cryptographic signature library (pure python)" +category = "main" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "exceptiongroup" +version = "1.0.4" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "mock" +version = "4.0.3" +description = "Rolling backport of unittest.mock for all Pythons" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +build = ["blurb", "twine", "wheel"] +docs = ["sphinx"] +test = ["pytest (<5.4)", "pytest-cov"] + +[[package]] +name = "mypy" +version = "0.991" +description = "Optional static typing for Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pyasn1" +version = "0.4.8" +description = "ASN.1 types and codecs" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "dev" +optional = false +python-versions = ">=3.6.8" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pytest" +version = "7.2.0" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.0.0" +description = "Pytest plugin for measuring coverage." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "requests" +version = "2.28.1" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7, <4" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<3" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +description = "OAuthlib authentication support for Requests." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +category = "main" +optional = false +python-versions = ">=3.6,<4" + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "typing-extensions" +version = "4.4.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "urllib3" +version = "1.26.13" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.10" +content-hash = "793d77edae00b65718473c862cee30345b593b874c16e9aa933bb190b41b16d0" + +[metadata.files] +attrs = [ + {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, + {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, +] +certifi = [ + {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"}, + {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, + {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, +] +colorama = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +coverage = [ + {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, + {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, + {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, + {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, + {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, + {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, + {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, + {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, + {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, + {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, + {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, + {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, + {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, + {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, + {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, +] +ecdsa = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] +exceptiongroup = [ + {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, + {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, +] +idna = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +mock = [ + {file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"}, + {file = "mock-4.0.3.tar.gz", hash = "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"}, +] +mypy = [ + {file = "mypy-0.991-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab"}, + {file = "mypy-0.991-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d"}, + {file = "mypy-0.991-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c8f3be99e8a8bd403caa8c03be619544bc2c77a7093685dcf308c6b109426c6"}, + {file = "mypy-0.991-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9ec663ed6c8f15f4ae9d3c04c989b744436c16d26580eaa760ae9dd5d662eb"}, + {file = "mypy-0.991-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4307270436fd7694b41f913eb09210faff27ea4979ecbcd849e57d2da2f65305"}, + {file = "mypy-0.991-cp310-cp310-win_amd64.whl", hash = "sha256:901c2c269c616e6cb0998b33d4adbb4a6af0ac4ce5cd078afd7bc95830e62c1c"}, + {file = "mypy-0.991-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d13674f3fb73805ba0c45eb6c0c3053d218aa1f7abead6e446d474529aafc372"}, + {file = "mypy-0.991-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c8cd4fb70e8584ca1ed5805cbc7c017a3d1a29fb450621089ffed3e99d1857f"}, + {file = "mypy-0.991-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:209ee89fbb0deed518605edddd234af80506aec932ad28d73c08f1400ef80a33"}, + {file = "mypy-0.991-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37bd02ebf9d10e05b00d71302d2c2e6ca333e6c2a8584a98c00e038db8121f05"}, + {file = "mypy-0.991-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:26efb2fcc6b67e4d5a55561f39176821d2adf88f2745ddc72751b7890f3194ad"}, + {file = "mypy-0.991-cp311-cp311-win_amd64.whl", hash = "sha256:3a700330b567114b673cf8ee7388e949f843b356a73b5ab22dd7cff4742a5297"}, + {file = "mypy-0.991-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1f7d1a520373e2272b10796c3ff721ea1a0712288cafaa95931e66aa15798813"}, + {file = "mypy-0.991-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:641411733b127c3e0dab94c45af15fea99e4468f99ac88b39efb1ad677da5711"}, + {file = "mypy-0.991-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3d80e36b7d7a9259b740be6d8d906221789b0d836201af4234093cae89ced0cd"}, + {file = "mypy-0.991-cp37-cp37m-win_amd64.whl", hash = "sha256:e62ebaad93be3ad1a828a11e90f0e76f15449371ffeecca4a0a0b9adc99abcef"}, + {file = "mypy-0.991-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b86ce2c1866a748c0f6faca5232059f881cda6dda2a893b9a8373353cfe3715a"}, + {file = "mypy-0.991-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac6e503823143464538efda0e8e356d871557ef60ccd38f8824a4257acc18d93"}, + {file = "mypy-0.991-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cca5adf694af539aeaa6ac633a7afe9bbd760df9d31be55ab780b77ab5ae8bf"}, + {file = "mypy-0.991-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12c56bf73cdab116df96e4ff39610b92a348cc99a1307e1da3c3768bbb5b135"}, + {file = "mypy-0.991-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:652b651d42f155033a1967739788c436491b577b6a44e4c39fb340d0ee7f0d70"}, + {file = "mypy-0.991-cp38-cp38-win_amd64.whl", hash = "sha256:4175593dc25d9da12f7de8de873a33f9b2b8bdb4e827a7cae952e5b1a342e243"}, + {file = "mypy-0.991-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98e781cd35c0acf33eb0295e8b9c55cdbef64fcb35f6d3aa2186f289bed6e80d"}, + {file = "mypy-0.991-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6d7464bac72a85cb3491c7e92b5b62f3dcccb8af26826257760a552a5e244aa5"}, + {file = "mypy-0.991-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c9166b3f81a10cdf9b49f2d594b21b31adadb3d5e9db9b834866c3258b695be3"}, + {file = "mypy-0.991-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8472f736a5bfb159a5e36740847808f6f5b659960115ff29c7cecec1741c648"}, + {file = "mypy-0.991-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e80e758243b97b618cdf22004beb09e8a2de1af481382e4d84bc52152d1c476"}, + {file = "mypy-0.991-cp39-cp39-win_amd64.whl", hash = "sha256:74e259b5c19f70d35fcc1ad3d56499065c601dfe94ff67ae48b85596b9ec1461"}, + {file = "mypy-0.991-py3-none-any.whl", hash = "sha256:de32edc9b0a7e67c2775e574cb061a537660e51210fbf6006b0b36ea695ae9bb"}, + {file = "mypy-0.991.tar.gz", hash = "sha256:3c0165ba8f354a6d9881809ef29f1a9318a236a6d81c690094c5df32107bde06"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +oauthlib = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +pyasn1 = [ + {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, + {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, +] +pyparsing = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] +pytest = [ + {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, + {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, +] +pytest-cov = [ + {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"}, + {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"}, +] +python-jose = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] +requests = [ + {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, + {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, +] +requests-oauthlib = [ + {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, + {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, +] +rsa = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +typing-extensions = [ + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, +] +urllib3 = [ + {file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"}, + {file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2229ecd --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,31 @@ +[tool.poetry] +name = "intuit-oauth-client" +version = "2.0.1" +description = "An updated fork of Intuit's python-oauthclient library" +authors = ["SunPowered "] +homepage = "https://github.com/SunPowered/intuit-oauth-pythonclient" +license = "Apache 2.0" +readme = "README.md" +packages = [{include = "intuitlib"}] + +[tool.poetry.dependencies] +python = "^3.10" +requests = "^2.13.0" +requests_oauthlib = "^1.0.0" +python_jose = "^3.3.0" + +[tool.poetry.group.dev.dependencies] +mypy = "^0.991" +pytest = "^7.2.0" +coverage = "^6.5.0" +pytest-cov = "^4.0.0" +mock = "^4.0.3" + +[tool.pytest.ini_options] +markers = [ + "http: marks tests that perform HTTP calls", +] + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 87b7bb0..0000000 --- a/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -python_jose>=2.0.2 -future>=0.16.0 -requests>=2.13.0 -mock>=2.0.0 -requests_oauthlib>=1.0.0 -coverage==4.4 -python-coveralls>=2.9.0 -pytest>=3.8.0 -pytest-cov==2.5.0 -six>=1.10.0 -enum-compat - - diff --git a/setup.py b/setup.py deleted file mode 100644 index 6ffc152..0000000 --- a/setup.py +++ /dev/null @@ -1,42 +0,0 @@ - # Copyright (c) 2018 Intuit - # - # Licensed under the Apache License, Version 2.0 (the "License"); - # you may not use this file except in compliance with the License. - # You may obtain a copy of the License at - # - # http://www.apache.org/licenses/LICENSE-2.0 - # - # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, - # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - # See the License for the specific language governing permissions and - # limitations under the License. - -from setuptools import setup -from setuptools import find_packages - -version = {} -with open("./intuitlib/version.py") as fp: - exec(fp.read(), version) - -setup( - name='intuit-oauth', - version=version['__version__'], - description='Intuit OAuth Client', - long_description=open('README.rst').read().strip(), - author='Intuit Inc', - author_email='IDGSDK@intuit.com', - url='https://github.com/intuit/oauth-pythonclient', - packages=find_packages(exclude=('tests*',)), - namespace_packages=('intuitlib',), - install_requires=[ - 'python_jose>=2.0.2', - 'future>=0.16.0', - 'requests>=2.13.0', - 'requests_oauthlib>=1.0.0', - 'six>=1.10.0', - 'enum-compat', - ], - license='Apache 2.0', - keywords='intuit quickbooks oauth auth openid client' -) diff --git a/tests/test_client.py b/tests/test_client.py index 5ebc106..4f2f241 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -15,7 +15,6 @@ """Test module for intuitlib.client """ -from __future__ import unicode_literals import pytest import mock @@ -29,37 +28,42 @@ from intuitlib.exceptions import AuthClientError from tests.helper import MockResponse -class TestClient(): - - auth_client = AuthClient('clientId','secret','https://www.mydemoapp.com/oauth-redirect','sandbox') +@pytest.fixture() +def auth_client_default(): + return AuthClient('clientId','secret','https://www.mydemoapp.com/oauth-redirect','sandbox') - client_mock_discovery_urls = { - 'authorization_endpoint': 'test', - 'token_endpoint': 'test', - 'revocation_endpoint': 'test', - 'issuer': 'test', - 'jwks_uri': 'test', - 'userinfo_endpoint': 'test', +@pytest.fixture() +def auth_client(auth_client_default): + auth_client_default._discovery_doc = { + 'authorization_endpoint': 'http://test', + 'token_endpoint': 'http://test', + 'revocation_endpoint': 'http://test', + 'issuer': 'http://test', + 'jwks_uri': 'http://test', + 'userinfo_endpoint': 'http://test', } + return auth_client_default +class TestClient(): + def mock_request(self, status=200, content=None): return MockResponse(status=status, content=content) - def test_input_all(self): + def test_input_all(self, auth_client): - self.auth_client.access_token = None + auth_client.access_token = None with pytest.raises(ValueError): - self.auth_client.refresh() + auth_client.refresh() with pytest.raises(ValueError): - self.auth_client.revoke() + auth_client.revoke() with pytest.raises(ValueError): - self.auth_client.get_user_info() + auth_client.get_user_info() - def test_get_authorization_url_without_csrf(self): - uri = self.auth_client.get_authorization_url([Scopes.ACCOUNTING]) + def test_get_authorization_url_without_csrf(self, auth_client): + uri = auth_client.get_authorization_url([Scopes.ACCOUNTING]) params = parse_qs(urlsplit(uri).query) param_values_to_string = {k: v[0] for k, v in params.items()} @@ -68,43 +72,44 @@ def test_get_authorization_url_without_csrf(self): 'response_type': 'code', 'scope': 'com.intuit.quickbooks.accounting', 'redirect_uri': 'https://www.mydemoapp.com/oauth-redirect', - 'state': self.auth_client.state_token + 'state': auth_client.state_token } assert auth_params == param_values_to_string - @mock.patch('intuitlib.utils.requests.Session') - def test_exceptions_all_bad_request(self, mock_post): + @mock.patch('intuitlib.utils.requests.Session.request') + def test_exceptions_all_bad_request(self, mock_post, auth_client): mock_resp = self.mock_request(status=400) mock_post.return_value = mock_resp + # auth_client = auth_client_default with pytest.raises(AuthClientError): - self.auth_client.get_bearer_token('test_code', realm_id='realm') + auth_client.get_bearer_token('test_code', realm_id='realm') with pytest.raises(AuthClientError): - self.auth_client.refresh(refresh_token='test_token') + auth_client.refresh(refresh_token='test_token') with pytest.raises(AuthClientError): - self.auth_client.revoke(token='test_token') + auth_client.revoke(token='test_token') with pytest.raises(AuthClientError): - self.auth_client.get_user_info(access_token='token') + auth_client.get_user_info(access_token='token') @mock.patch('intuitlib.utils.requests.Session.request') - def test_get_user_info_ok(self, mock_session): + def test_get_user_info_ok(self, mock_session, auth_client): mock_resp = self.mock_request(status=200, content={ 'givenName': 'Test' }) mock_session.return_value = mock_resp - response = self.auth_client.get_user_info(access_token='token') + response = auth_client.get_user_info(access_token='token') assert response.json()['givenName'] == 'Test' @mock.patch('intuitlib.utils.requests.Session.request') - def test_revoke_ok(self, mock_session): + def test_revoke_ok(self, mock_session, auth_client): mock_resp = self.mock_request(status=200) mock_session.return_value = mock_resp - response = self.auth_client.revoke(token='token') + response = auth_client.revoke(token='token') assert response if __name__ == '__main__': diff --git a/tests/test_migration.py b/tests/test_migration.py index 520f0ae..5b3712d 100644 --- a/tests/test_migration.py +++ b/tests/test_migration.py @@ -31,49 +31,54 @@ from intuitlib.migration import migrate from tests.helper import MockResponse -class TestMigration(): - - auth_client = AuthClient('clientId','secret','https://www.mydemoapp.com/oauth-redirect','sandbox') - - client_mock_discovery_urls = { - 'authorization_endpoint': 'test', - 'token_endpoint': 'test', - 'revocation_endpoint': 'test', - 'issuer': 'test', - 'jwks_uri': 'test', - 'userinfo_endpoint': 'test', +@pytest.fixture() +def auth_client_default(): + return AuthClient('clientId','secret','https://www.mydemoapp.com/oauth-redirect','sandbox') + +@pytest.fixture() +def auth_client(auth_client_default): + auth_client_default._discovery_doc = { + 'authorization_endpoint': 'http://test', + 'token_endpoint': 'http://test', + 'revocation_endpoint': 'http://test', + 'issuer': 'http://test', + 'jwks_uri': 'http://test', + 'userinfo_endpoint': 'http://test', } + return auth_client_default + +class TestMigration(): def mock_request(self, status=200, content=None): return MockResponse(status=status, content=content) @mock.patch('intuitlib.utils.requests.request') - def test_migrate_bad_request(self, mock_post): + def test_migrate_bad_request(self, mock_post, auth_client): mock_resp = self.mock_request(status=400) mock_post.return_value = mock_resp with pytest.raises(AuthClientError): - migrate('consumer_key', 'consumer_secret', 'access_token', 'access_secret', self.auth_client, [Scopes.ACCOUNTING]) + migrate('consumer_key', 'consumer_secret', 'access_token', 'access_secret', auth_client, [Scopes.ACCOUNTING]) @mock.patch('intuitlib.utils.requests.request') - def test_migrate_200(self, mock_post): + def test_migrate_200(self, mock_post, auth_client): mock_resp = self.mock_request(status=200, content={ 'access_token': 'testaccess' }) mock_post.return_value = mock_resp - migrate('consumer_key', 'consumer_secret', 'access_token', 'access_secret', self.auth_client, [Scopes.ACCOUNTING]) + migrate('consumer_key', 'consumer_secret', 'access_token', 'access_secret', auth_client, [Scopes.ACCOUNTING]) - assert self.auth_client.access_token == 'testaccess' + assert auth_client.access_token == 'testaccess' @mock.patch('intuitlib.utils.requests.request') - def test_migrate_prod(self, mock_post): + def test_migrate_prod(self, mock_post, auth_client): mock_resp = self.mock_request(status=400) mock_post.return_value = mock_resp - self.auth_client.environment = 'production' + auth_client.environment = 'production' with pytest.raises(AuthClientError): - migrate('consumer_key', 'consumer_secret', 'access_token', 'access_secret', self.auth_client, [Scopes.ACCOUNTING]) + migrate('consumer_key', 'consumer_secret', 'access_token', 'access_secret', auth_client, [Scopes.ACCOUNTING]) if __name__ == '__main__': pytest.main() \ No newline at end of file diff --git a/tests/test_utils.py b/tests/test_utils.py index 9cf2059..a16f845 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -33,6 +33,23 @@ from intuitlib.exceptions import AuthClientError from tests.helper import MockResponse +@pytest.fixture() +def auth_client_default(): + return AuthClient('clientId','secret','https://www.mydemoapp.com/oauth-redirect','sandbox') + +@pytest.fixture() +def auth_client(auth_client_default): + auth_client_default._discovery_doc = { + 'authorization_endpoint': 'http://test', + 'token_endpoint': 'http://test', + 'revocation_endpoint': 'http://test', + 'issuer': 'http://test', + 'jwks_uri': 'http://test', + 'userinfo_endpoint': 'http://test', + } + return auth_client_default + + class TestUtils(): auth_client = AuthClient('client_id','client_secret','redirect_uri','sandbox') @@ -40,18 +57,21 @@ class TestUtils(): def mock_request(self, status=200, content=None): return MockResponse(status=status, content=content) + @pytest.mark.http def test_get_discovery_doc_sandbox(self): discovery_doc = get_discovery_doc('sandbox') assert discovery_doc['issuer'] == 'https://oauth.platform.intuit.com/op/v1' assert discovery_doc['userinfo_endpoint'] == 'https://sandbox-accounts.platform.intuit.com/v1/openid_connect/userinfo' + @pytest.mark.http def test_get_discovery_doc_production(self): discovery_doc = get_discovery_doc('production') assert discovery_doc['issuer'] == 'https://oauth.platform.intuit.com/op/v1' assert discovery_doc['userinfo_endpoint'] == 'https://accounts.platform.intuit.com/v1/openid_connect/userinfo' + @pytest.mark.http def test_get_discovery_doc_custom_url_input(self): discovery_doc = get_discovery_doc('https://developer.intuit.com/.well-known/openid_sandbox_configuration/') @@ -79,18 +99,18 @@ def test_scopes_to_string_input_correct(self): assert scope == 'openid email' - def test_set_attributes(self): + def test_set_attributes(self, auth_client): response = { 'refresh_token': 'testrefresh', 'access_token': 'testaccess', 'test': 'testing', 'id_token': 'token' } - set_attributes(self.auth_client, response) + set_attributes(auth_client, response) - assert self.auth_client.refresh_token == response['refresh_token'] - assert self.auth_client.access_token == response['access_token'] - assert not self.auth_client.id_token + assert auth_client.refresh_token == response['refresh_token'] + assert auth_client.access_token == response['access_token'] + assert not auth_client.id_token @mock.patch('intuitlib.utils.requests.request') def test_send_request_bad_request(self, mock_post): @@ -101,30 +121,30 @@ def test_send_request_bad_request(self, mock_post): send_request('POST', 'url', {}, '', body={}) @mock.patch('intuitlib.utils.requests.request') - def test_send_request_ok(self, mock_post): + def test_send_request_ok(self, mock_post, auth_client): mock_resp = self.mock_request(status=200, content={'access_token': 'testaccess'}) mock_post.return_value = mock_resp - send_request('POST', 'url', {}, self.auth_client, body={}) - assert self.auth_client.access_token == 'testaccess' + send_request('POST', 'url', {}, auth_client, body={}) + assert auth_client.access_token == 'testaccess' @mock.patch('intuitlib.utils.Session.request') - def test_send_request_session_ok(self, mock_post): + def test_send_request_session_ok(self, mock_post, auth_client): mock_resp = self.mock_request(status=200, content={'access_token': 'testaccess'}) mock_post.return_value = mock_resp session = requests.Session() - send_request('POST', 'url', {}, self.auth_client, body={}, session=session) - assert self.auth_client.access_token == 'testaccess' + send_request('POST', 'url', {}, auth_client, body={}, session=session) + assert auth_client.access_token == 'testaccess' @mock.patch('intuitlib.utils.Session.request') - def test_send_request_session_bad(self, mock_post): + def test_send_request_session_bad(self, mock_post, auth_client): mock_resp = self.mock_request(status=400, content={'access_token': 'testaccess'}) mock_post.return_value = mock_resp session = requests.Session() with pytest.raises(AuthClientError): - send_request('POST', 'url', {}, self.auth_client, body={}, session=session) + send_request('POST', 'url', {}, auth_client, body={}, session=session) def test_generate_token(self): token = generate_token()