diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 43417ee5..b36e018a 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -1,4 +1,4 @@ -name: Production deploy +name: Publish release on: workflow_run: workflows: ["Python Tests"] @@ -6,10 +6,7 @@ on: - completed jobs: pypi-publish: - if: | - github.repository_owner == 'oauthlib' && - ${{ github.event.workflow_run.conclusion == 'success' }} && - ${{ github.ref_type == 'tag' }} + if: github.repository_owner == 'oauthlib' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags') name: Upload release to PyPI runs-on: ubuntu-latest environment: @@ -19,8 +16,10 @@ jobs: id-token: write # IMPORTANT: this permission is mandatory for trusted publishing steps: - name: Check out repository code - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + uses: actions/checkout@v4 + with: # by default, this event will trigger a workflow on the default branch. + ref: ${{ github.event.workflow_run.head_ref }} # set source branch + - uses: actions/setup-python@v5 with: python-version: '3.10' - name: Install prereq diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 82dbd75a..8064cd0d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,30 @@ Changelog ========= +3.3.0 (2025-06-17): +------------------ +OAuth2.0 Provider: +* OIDC: #879 Changed in how ui_locales is parsed +* RFC8628: Added OAuth2.0 Device Authorization Grant support +* PKCE: #876, #893 Fixed `create_code_verifier` length +* OIDC: Pre-configured OIDC server to use Refresh Token by default + +OAuth2.0 Common: +* OAuth2Error: Allow 0 to be a valid state + +OAuth2.0 Client: +* #745: expires_at is forced to be an int +* #899: expires_at clarification + +General: +* Removed Python 3.5, 3.6, 3.7 support +* #859, #883: Added Python 3.12, 3.13 Support +* Added dependency-review GitHub Action +* Updated various references of license (SPDX identifier..) +* Added GitHub Action for lint, replaced bandy with ruff, removed isort... +* Migrated to GitHub Actions from Travis +* Added Security Policy + 3.2.2 (2022-10-17) ------------------ OAuth2.0 Provider: diff --git a/LICENSE b/LICENSE index d5a9e9ac..ffab1267 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019 The OAuthlib Community +Copyright (c) The OAuthlib Community All rights reserved. Redistribution and use in source and binary forms, with or without @@ -11,14 +11,14 @@ modification, are permitted provided that the following conditions are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - 3. Neither the name of this project nor the names of its contributors may - be used to endorse or promote products derived from this software without - specific prior written permission. + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER diff --git a/Makefile b/Makefile index 550525c6..9622f70d 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ # # Please specify your library as well as primary contacts. # Since these contacts will be addressed with Github mentions they -# need to be Github users (for now)(sorry Bitbucket). +# need to be Github users. # clean: clean-eggs clean-build @find . -iname '*.pyc' -delete @@ -45,25 +45,18 @@ test: bottle: #--------------------------- - # Library thomsonreuters/bottle-oauthlib + # Library refinitiv/bottle-oauthlib # Contacts: Jonathan.Huot - cd bottle-oauthlib 2>/dev/null || git clone https://github.com/thomsonreuters/bottle-oauthlib.git + cd bottle-oauthlib 2>/dev/null || git clone https://github.com/refinitiv/bottle-oauthlib.git cd bottle-oauthlib && sed -i.old 's,deps =,deps= --editable=file://{toxinidir}/../,' tox.ini && sed -i.old '/oauthlib/d' requirements.txt && tox -flask: - #--------------------------- - # Library: lepture/flask-oauthLib - # Contacts: lepture,widnyana - cd flask-oauthlib 2>/dev/null || git clone https://github.com/lepture/flask-oauthlib.git - cd flask-oauthlib && sed -i.old 's,deps =,deps= --editable=file://{toxinidir}/../,' tox.ini && sed -i.old '/oauthlib/d' requirements.txt && tox - django: #--------------------------- # Library: evonove/django-oauth-toolkit # Contacts: evonove,masci # (note: has tox.ini already) cd django-oauth-toolkit 2>/dev/null || git clone https://github.com/evonove/django-oauth-toolkit.git - cd django-oauth-toolkit && sed -i.old 's,deps =,deps= --editable=file://{toxinidir}/../,' tox.ini && tox -e py27,py35,py36 + cd django-oauth-toolkit && sed -i.old 's,deps =,deps= --editable=file://{toxinidir}/../,' tox.ini && tox requests: #--------------------------- diff --git a/README.rst b/README.rst index ef0b5ad7..840c33d5 100644 --- a/README.rst +++ b/README.rst @@ -76,8 +76,8 @@ Which web frameworks are supported? The following packages provide OAuth support using OAuthLib. - For Django there is: - - `django-oauth-toolkit`_, which includes `Django REST framework`_ support. - - `django-allauth`_, which includes `Django REST framework`_ as well as `Django Ninja`_ support. + - `django-oauth-toolkit`_, which includes `Django REST framework`_ support. + - `django-allauth`_, which includes `Django REST framework`_ as well as `Django Ninja`_ support. - For Flask there is `flask-oauthlib`_ and `Flask-Dance`_. - For Pyramid there is `pyramid-oauthlib`_. - For Bottle there is `bottle-oauthlib`_. @@ -114,7 +114,7 @@ have the pleasure to run into each other, please send a docs pull request =) License ------- -OAuthLib is yours to use and abuse according to the terms of the BSD license. +OAuthLib is yours to use and abuse according to the terms of the BSD-3-Clause license. Check the LICENSE file for full details. Credits diff --git a/SECURITY.md b/SECURITY.md index ddb8632d..f1a3fb80 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,13 +2,26 @@ ## Supported Versions -following versions are currently being supported with security updates. +The following versions are currently being supported with security updates. | Version | Supported | | ------- | ------------------ | +| 3.3.x | :white_check_mark: | | 3.2.x | :white_check_mark: | | 3.1.x | :x: | -| < 3.2.0 | :x: | +| < 3.1 | :x: | ## Reporting a Vulnerability -Contact auvipy@gmail.com for reporting any vulnerability. + +Please raise a draft advisory to start discussing about the vulnerability in a private channel with OAuthlib Admin: https://github.com/oauthlib/oauthlib/security/advisories/new + +## Incident Response Plan + +The Incident Response Plan for oauthlib is composed of four steps: + +- Triage: discussion about the validity of the vulnerability with the reporter + in the private channel. +- Mitigate: work on a fix and release a newer version. +- Disclose: let downstream applications some time to update to the latest + release, then make the CVE public. +- Learn: discuss about any potential actions that could have prevented the vulnerability. diff --git a/bandit.json b/bandit.json deleted file mode 100644 index 7161f005..00000000 --- a/bandit.json +++ /dev/null @@ -1,1222 +0,0 @@ -{ - "errors": [], - "generated_at": "2019-05-13T12:51:49Z", - "metrics": { - "_totals": { - "CONFIDENCE.HIGH": 3.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 10.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 12.0, - "SEVERITY.MEDIUM": 1.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 8338, - "nosec": 0 - }, - "oauthlib/__init__.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 25, - "nosec": 0 - }, - "oauthlib/common.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 337, - "nosec": 0 - }, - "oauthlib/oauth1/__init__.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 16, - "nosec": 0 - }, - "oauthlib/oauth1/rfc5849/__init__.py": { - "CONFIDENCE.HIGH": 1.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 1.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 230, - "nosec": 0 - }, - "oauthlib/oauth1/rfc5849/endpoints/__init__.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 8, - "nosec": 0 - }, - "oauthlib/oauth1/rfc5849/endpoints/access_token.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 152, - "nosec": 0 - }, - "oauthlib/oauth1/rfc5849/endpoints/authorization.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 135, - "nosec": 0 - }, - "oauthlib/oauth1/rfc5849/endpoints/base.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 142, - "nosec": 0 - }, - "oauthlib/oauth1/rfc5849/endpoints/pre_configured.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 10, - "nosec": 0 - }, - "oauthlib/oauth1/rfc5849/endpoints/request_token.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 141, - "nosec": 0 - }, - "oauthlib/oauth1/rfc5849/endpoints/resource.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 97, - "nosec": 0 - }, - "oauthlib/oauth1/rfc5849/endpoints/signature_only.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 53, - "nosec": 0 - }, - "oauthlib/oauth1/rfc5849/errors.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 58, - "nosec": 0 - }, - "oauthlib/oauth1/rfc5849/parameters.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 75, - "nosec": 0 - }, - "oauthlib/oauth1/rfc5849/request_validator.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 630, - "nosec": 0 - }, - "oauthlib/oauth1/rfc5849/signature.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 379, - "nosec": 0 - }, - "oauthlib/oauth1/rfc5849/utils.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 61, - "nosec": 0 - }, - "oauthlib/oauth2/__init__.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 33, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/__init__.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 14, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/clients/__init__.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 13, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/clients/backend_application.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 56, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/clients/base.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 3.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 3.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 384, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/clients/legacy_application.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 67, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/clients/mobile_application.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 140, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/clients/service_application.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 144, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/clients/web_application.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 165, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/endpoints/__init__.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 18, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/endpoints/authorization.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 85, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/endpoints/base.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 71, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/endpoints/introspect.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 98, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/endpoints/metadata.py": { - "CONFIDENCE.HIGH": 2.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 2.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 182, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/endpoints/pre_configured.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 5.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 5.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 189, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/endpoints/resource.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 65, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/endpoints/revocation.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 96, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/endpoints/token.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 76, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/errors.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 311, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/grant_types/__init__.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 10, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/grant_types/authorization_code.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 389, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/grant_types/base.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 199, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/grant_types/client_credentials.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 96, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/grant_types/implicit.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 259, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/grant_types/refresh_token.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 102, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 156, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/parameters.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 1.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 1.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 335, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/request_validator.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 504, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/tokens.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 277, - "nosec": 0 - }, - "oauthlib/oauth2/rfc6749/utils.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 70, - "nosec": 0 - }, - "oauthlib/openid/__init__.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 8, - "nosec": 0 - }, - "oauthlib/openid/connect/__init__.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 0, - "nosec": 0 - }, - "oauthlib/openid/connect/core/__init__.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 0, - "nosec": 0 - }, - "oauthlib/openid/connect/core/endpoints/__init__.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 9, - "nosec": 0 - }, - "oauthlib/openid/connect/core/endpoints/pre_configured.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 1.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 1.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 93, - "nosec": 0 - }, - "oauthlib/openid/connect/core/endpoints/userinfo.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 83, - "nosec": 0 - }, - "oauthlib/openid/connect/core/exceptions.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 117, - "nosec": 0 - }, - "oauthlib/openid/connect/core/grant_types/__init__.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 15, - "nosec": 0 - }, - "oauthlib/openid/connect/core/grant_types/authorization_code.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 32, - "nosec": 0 - }, - "oauthlib/openid/connect/core/grant_types/base.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 234, - "nosec": 0 - }, - "oauthlib/openid/connect/core/grant_types/dispatchers.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 66, - "nosec": 0 - }, - "oauthlib/openid/connect/core/grant_types/exceptions.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 26, - "nosec": 0 - }, - "oauthlib/openid/connect/core/grant_types/hybrid.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 38, - "nosec": 0 - }, - "oauthlib/openid/connect/core/grant_types/implicit.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 35, - "nosec": 0 - }, - "oauthlib/openid/connect/core/request_validator.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 235, - "nosec": 0 - }, - "oauthlib/openid/connect/core/tokens.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 42, - "nosec": 0 - }, - "oauthlib/signals.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 32, - "nosec": 0 - }, - "oauthlib/tokens/__init__.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 0, - "nosec": 0 - }, - "oauthlib/tokens/access_token.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 13, - "nosec": 0 - }, - "oauthlib/tokens/base.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 8, - "nosec": 0 - }, - "oauthlib/tokens/id_token.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 6, - "nosec": 0 - }, - "oauthlib/uri_validate.py": { - "CONFIDENCE.HIGH": 0.0, - "CONFIDENCE.LOW": 0.0, - "CONFIDENCE.MEDIUM": 0.0, - "CONFIDENCE.UNDEFINED": 0.0, - "SEVERITY.HIGH": 0.0, - "SEVERITY.LOW": 0.0, - "SEVERITY.MEDIUM": 0.0, - "SEVERITY.UNDEFINED": 0.0, - "loc": 93, - "nosec": 0 - } - }, - "results": [ - { - "code": "183 if request.body is not None and content_type_eligible:\n184 params.append(('oauth_body_hash', base64.b64encode(hashlib.sha1(request.body.encode('utf-8')).digest()).decode('utf-8')))\n185 \n", - "filename": "oauthlib/oauth1/rfc5849/__init__.py", - "issue_confidence": "HIGH", - "issue_severity": "MEDIUM", - "issue_text": "Use of insecure MD2, MD4, MD5, or SHA1 hash function.", - "line_number": 184, - "line_range": [ - 184 - ], - "more_info": "https://bandit.readthedocs.io/en/latest/blacklists/blacklist_calls.html#b303-md5", - "test_id": "B303", - "test_name": "blacklist" - }, - { - "code": "49 \"\"\"\n50 refresh_token_key = 'refresh_token'\n51 \n52 def __init__(self, client_id,\n", - "filename": "oauthlib/oauth2/rfc6749/clients/base.py", - "issue_confidence": "MEDIUM", - "issue_severity": "LOW", - "issue_text": "Possible hardcoded password: 'refresh_token'", - "line_number": 50, - "line_range": [ - 50, - 51 - ], - "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b105_hardcoded_password_string.html", - "test_id": "B105", - "test_name": "hardcoded_password_string" - }, - { - "code": "51 \n52 def __init__(self, client_id,\n53 default_token_placement=AUTH_HEADER,\n54 token_type='Bearer',\n55 access_token=None,\n56 refresh_token=None,\n57 mac_key=None,\n58 mac_algorithm=None,\n59 token=None,\n60 scope=None,\n61 state=None,\n62 redirect_url=None,\n63 state_generator=generate_token,\n64 **kwargs):\n65 \"\"\"Initialize a client with commonly used attributes.\n66 \n67 :param client_id: Client identifier given by the OAuth provider upon\n68 registration.\n69 \n70 :param default_token_placement: Tokens can be supplied in the Authorization\n71 header (default), the URL query component (``query``) or the request\n72 body (``body``).\n73 \n74 :param token_type: OAuth 2 token type. Defaults to Bearer. Change this\n75 if you specify the ``access_token`` parameter and know it is of a\n76 different token type, such as a MAC, JWT or SAML token. Can\n77 also be supplied as ``token_type`` inside the ``token`` dict parameter.\n78 \n79 :param access_token: An access token (string) used to authenticate\n80 requests to protected resources. Can also be supplied inside the\n81 ``token`` dict parameter.\n82 \n83 :param refresh_token: A refresh token (string) used to refresh expired\n84 tokens. Can also be supplied inside the ``token`` dict parameter.\n85 \n86 :param mac_key: Encryption key used with MAC tokens.\n87 \n88 :param mac_algorithm: Hashing algorithm for MAC tokens.\n89 \n90 :param token: A dict of token attributes such as ``access_token``,\n91 ``token_type`` and ``expires_at``.\n92 \n93 :param scope: A list of default scopes to request authorization for.\n94 \n95 :param state: A CSRF protection string used during authorization.\n96 \n97 :param redirect_url: The redirection endpoint on the client side to which\n98 the user returns after authorization.\n99 \n100 :param state_generator: A no argument state generation callable. Defaults\n101 to :py:meth:`oauthlib.common.generate_token`.\n102 \"\"\"\n103 \n104 self.client_id = client_id\n105 self.default_token_placement = default_token_placement\n106 self.token_type = token_type\n107 self.access_token = access_token\n108 self.refresh_token = refresh_token\n109 self.mac_key = mac_key\n110 self.mac_algorithm = mac_algorithm\n111 self.token = token or {}\n112 self.scope = scope\n113 self.state_generator = state_generator\n114 self.state = state\n115 self.redirect_url = redirect_url\n116 self.code = None\n117 self.expires_in = None\n118 self._expires_at = None\n119 self.populate_token_attributes(self.token)\n120 \n121 @property\n", - "filename": "oauthlib/oauth2/rfc6749/clients/base.py", - "issue_confidence": "MEDIUM", - "issue_severity": "LOW", - "issue_text": "Possible hardcoded password: 'Bearer'", - "line_number": 52, - "line_range": [ - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 91, - 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 100, - 101, - 102, - 103, - 104, - 105, - 106, - 107, - 108, - 109, - 110, - 111, - 112, - 113, - 114, - 115, - 116, - 117, - 118, - 119, - 120 - ], - "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b107_hardcoded_password_default.html", - "test_id": "B107", - "test_name": "hardcoded_password_default" - }, - { - "code": "313 \n314 def prepare_token_revocation_request(self, revocation_url, token,\n315 token_type_hint=\"access_token\", body='', callback=None, **kwargs):\n316 \"\"\"Prepare a token revocation request.\n317 \n318 :param revocation_url: Provider token revocation endpoint URL.\n319 \n320 :param token: The access or refresh token to be revoked (string).\n321 \n322 :param token_type_hint: ``\"access_token\"`` (default) or\n323 ``\"refresh_token\"``. This is optional and if you wish to not pass it you\n324 must provide ``token_type_hint=None``.\n325 \n326 :param body:\n327 \n328 :param callback: A jsonp callback such as ``package.callback`` to be invoked\n329 upon receiving the response. Not that it should not include a () suffix.\n330 \n331 :param kwargs: Additional parameters to included in the request.\n332 \n333 :returns: The prepared request tuple with (url, headers, body).\n334 \n335 Note that JSONP request may use GET requests as the parameters will\n336 be added to the request URL query as opposed to the request body.\n337 \n338 An example of a revocation request\n339 \n340 .. code-block: http\n341 \n342 POST /revoke HTTP/1.1\n343 Host: server.example.com\n344 Content-Type: application/x-www-form-urlencoded\n345 Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW\n346 \n347 token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token\n348 \n349 An example of a jsonp revocation request\n350 \n351 .. code-block: http\n352 \n353 GET /revoke?token=agabcdefddddafdd&callback=package.myCallback HTTP/1.1\n354 Host: server.example.com\n355 Content-Type: application/x-www-form-urlencoded\n356 Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW\n357 \n358 and an error response\n359 \n360 .. code-block: http\n361 \n362 package.myCallback({\"error\":\"unsupported_token_type\"});\n363 \n364 Note that these requests usually require client credentials, client_id in\n365 the case for public clients and provider specific authentication\n366 credentials for confidential clients.\n367 \"\"\"\n368 if not is_secure_transport(revocation_url):\n369 raise InsecureTransportError()\n370 \n371 return prepare_token_revocation_request(revocation_url, token,\n372 token_type_hint=token_type_hint, body=body, callback=callback,\n373 **kwargs)\n374 \n375 def parse_request_body_response(self, body, scope=None, **kwargs):\n", - "filename": "oauthlib/oauth2/rfc6749/clients/base.py", - "issue_confidence": "MEDIUM", - "issue_severity": "LOW", - "issue_text": "Possible hardcoded password: 'access_token'", - "line_number": 314, - "line_range": [ - 314, - 315, - 316, - 317, - 318, - 319, - 320, - 321, - 322, - 323, - 324, - 325, - 326, - 327, - 328, - 329, - 330, - 331, - 332, - 333, - 334, - 335, - 336, - 337, - 338, - 339, - 340, - 341, - 342, - 343, - 344, - 345, - 346, - 347, - 348, - 349, - 350, - 351, - 352, - 353, - 354, - 355, - 356, - 357, - 358, - 359, - 360, - 361, - 362, - 363, - 364, - 365, - 366, - 367, - 368, - 369, - 370, - 371, - 372, - 373, - 374 - ], - "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b107_hardcoded_password_default.html", - "test_id": "B107", - "test_name": "hardcoded_password_default" - }, - { - "code": "45 def __init__(self, endpoints, claims={}, raise_errors=True):\n46 assert isinstance(claims, dict)\n47 for endpoint in endpoints:\n", - "filename": "oauthlib/oauth2/rfc6749/endpoints/metadata.py", - "issue_confidence": "HIGH", - "issue_severity": "LOW", - "issue_text": "Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.", - "line_number": 46, - "line_range": [ - 46 - ], - "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b101_assert_used.html", - "test_id": "B101", - "test_name": "assert_used" - }, - { - "code": "47 for endpoint in endpoints:\n48 assert isinstance(endpoint, BaseEndpoint)\n49 \n", - "filename": "oauthlib/oauth2/rfc6749/endpoints/metadata.py", - "issue_confidence": "HIGH", - "issue_severity": "LOW", - "issue_text": "Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.", - "line_number": 48, - "line_range": [ - 48 - ], - "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b101_assert_used.html", - "test_id": "B101", - "test_name": "assert_used" - }, - { - "code": "70 default_token_type=bearer)\n71 ResourceEndpoint.__init__(self, default_token='Bearer',\n72 token_types={'Bearer': bearer})\n73 RevocationEndpoint.__init__(self, request_validator)\n", - "filename": "oauthlib/oauth2/rfc6749/endpoints/pre_configured.py", - "issue_confidence": "MEDIUM", - "issue_severity": "LOW", - "issue_text": "Possible hardcoded password: 'Bearer'", - "line_number": 71, - "line_range": [ - 71, - 72 - ], - "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b106_hardcoded_password_funcarg.html", - "test_id": "B106", - "test_name": "hardcoded_password_funcarg" - }, - { - "code": "109 default_token_type=bearer)\n110 ResourceEndpoint.__init__(self, default_token='Bearer',\n111 token_types={'Bearer': bearer})\n112 RevocationEndpoint.__init__(self, request_validator)\n", - "filename": "oauthlib/oauth2/rfc6749/endpoints/pre_configured.py", - "issue_confidence": "MEDIUM", - "issue_severity": "LOW", - "issue_text": "Possible hardcoded password: 'Bearer'", - "line_number": 110, - "line_range": [ - 110, - 111 - ], - "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b106_hardcoded_password_funcarg.html", - "test_id": "B106", - "test_name": "hardcoded_password_funcarg" - }, - { - "code": "142 default_token_type=bearer)\n143 ResourceEndpoint.__init__(self, default_token='Bearer',\n144 token_types={'Bearer': bearer})\n145 RevocationEndpoint.__init__(self, request_validator,\n", - "filename": "oauthlib/oauth2/rfc6749/endpoints/pre_configured.py", - "issue_confidence": "MEDIUM", - "issue_severity": "LOW", - "issue_text": "Possible hardcoded password: 'Bearer'", - "line_number": 143, - "line_range": [ - 143, - 144 - ], - "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b106_hardcoded_password_funcarg.html", - "test_id": "B106", - "test_name": "hardcoded_password_funcarg" - }, - { - "code": "181 default_token_type=bearer)\n182 ResourceEndpoint.__init__(self, default_token='Bearer',\n183 token_types={'Bearer': bearer})\n184 RevocationEndpoint.__init__(self, request_validator)\n", - "filename": "oauthlib/oauth2/rfc6749/endpoints/pre_configured.py", - "issue_confidence": "MEDIUM", - "issue_severity": "LOW", - "issue_text": "Possible hardcoded password: 'Bearer'", - "line_number": 182, - "line_range": [ - 182, - 183 - ], - "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b106_hardcoded_password_funcarg.html", - "test_id": "B106", - "test_name": "hardcoded_password_funcarg" - }, - { - "code": "214 default_token_type=bearer)\n215 ResourceEndpoint.__init__(self, default_token='Bearer',\n216 token_types={'Bearer': bearer})\n217 RevocationEndpoint.__init__(self, request_validator,\n", - "filename": "oauthlib/oauth2/rfc6749/endpoints/pre_configured.py", - "issue_confidence": "MEDIUM", - "issue_severity": "LOW", - "issue_text": "Possible hardcoded password: 'Bearer'", - "line_number": 215, - "line_range": [ - 215, - 216 - ], - "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b106_hardcoded_password_funcarg.html", - "test_id": "B106", - "test_name": "hardcoded_password_funcarg" - }, - { - "code": "164 \n165 def prepare_token_revocation_request(url, token, token_type_hint=\"access_token\",\n166 callback=None, body='', **kwargs):\n167 \"\"\"Prepare a token revocation request.\n168 \n169 The client constructs the request by including the following parameters\n170 using the \"application/x-www-form-urlencoded\" format in the HTTP request\n171 entity-body:\n172 \n173 :param token: REQUIRED. The token that the client wants to get revoked.\n174 \n175 :param token_type_hint: OPTIONAL. A hint about the type of the token\n176 submitted for revocation. Clients MAY pass this\n177 parameter in order to help the authorization server\n178 to optimize the token lookup. If the server is\n179 unable to locate the token using the given hint, it\n180 MUST extend its search across all of its supported\n181 token types. An authorization server MAY ignore\n182 this parameter, particularly if it is able to detect\n183 the token type automatically.\n184 \n185 This specification defines two values for `token_type_hint`:\n186 \n187 * access_token: An access token as defined in [RFC6749],\n188 `Section 1.4`_\n189 \n190 * refresh_token: A refresh token as defined in [RFC6749],\n191 `Section 1.5`_\n192 \n193 Specific implementations, profiles, and extensions of this\n194 specification MAY define other values for this parameter using the\n195 registry defined in `Section 4.1.2`_.\n196 \n197 .. _`Section 1.4`: https://tools.ietf.org/html/rfc6749#section-1.4\n198 .. _`Section 1.5`: https://tools.ietf.org/html/rfc6749#section-1.5\n199 .. _`Section 4.1.2`: https://tools.ietf.org/html/rfc7009#section-4.1.2\n200 \n201 \"\"\"\n202 if not is_secure_transport(url):\n203 raise InsecureTransportError()\n204 \n205 params = [('token', token)]\n206 \n207 if token_type_hint:\n208 params.append(('token_type_hint', token_type_hint))\n209 \n210 for k in kwargs:\n211 if kwargs[k]:\n212 params.append((str(k), kwargs[k]))\n213 \n214 headers = {'Content-Type': 'application/x-www-form-urlencoded'}\n215 \n216 if callback:\n217 params.append(('callback', callback))\n218 return add_params_to_uri(url, params), headers, body\n219 else:\n220 return url, headers, add_params_to_qs(body, params)\n221 \n222 \n223 def parse_authorization_code_response(uri, state=None):\n", - "filename": "oauthlib/oauth2/rfc6749/parameters.py", - "issue_confidence": "MEDIUM", - "issue_severity": "LOW", - "issue_text": "Possible hardcoded password: 'access_token'", - "line_number": 165, - "line_range": [ - 165, - 166, - 167, - 168, - 169, - 170, - 171, - 172, - 173, - 174, - 175, - 176, - 177, - 178, - 179, - 180, - 181, - 182, - 183, - 184, - 185, - 186, - 187, - 188, - 189, - 190, - 191, - 192, - 193, - 194, - 195, - 196, - 197, - 198, - 199, - 200, - 201, - 202, - 203, - 204, - 205, - 206, - 207, - 208, - 209, - 210, - 211, - 212, - 213, - 214, - 215, - 216, - 217, - 218, - 219, - 220, - 221, - 222 - ], - "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b107_hardcoded_password_default.html", - "test_id": "B107", - "test_name": "hardcoded_password_default" - }, - { - "code": "104 default_token_type=bearer)\n105 ResourceEndpoint.__init__(self, default_token='Bearer',\n106 token_types={'Bearer': bearer, 'JWT': jwt})\n107 RevocationEndpoint.__init__(self, request_validator)\n", - "filename": "oauthlib/openid/connect/core/endpoints/pre_configured.py", - "issue_confidence": "MEDIUM", - "issue_severity": "LOW", - "issue_text": "Possible hardcoded password: 'Bearer'", - "line_number": 105, - "line_range": [ - 105, - 106 - ], - "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b106_hardcoded_password_funcarg.html", - "test_id": "B106", - "test_name": "hardcoded_password_funcarg" - } - ] -} \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 05e93ee0..e609c541 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -46,7 +46,7 @@ # General information about the project. project = 'OAuthLib' -copyright = '2019, The OAuthlib Community' +copyright = 'The OAuthlib Community' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/docs/release_process.rst b/docs/release_process.rst index 2796f29c..70cc1a5f 100644 --- a/docs/release_process.rst +++ b/docs/release_process.rst @@ -51,3 +51,29 @@ Minor point (1.1.0) releases will introduce non API breaking new features and changes. Bug releases (1.0.1) will include minor fixes that needs to be released quickly (e.g. after a bigger release unintentionally introduced a bug). + +For maintainer - Publishing a newer version +-------------------------------------------- + +List of tasks to do a release from a maintainer point of view: + + - Create a Branch ``xyz-release`` + - Update ``oauthlib/__init__.py`` version + - Update ``CHANGELOG.rst`` accordingly + - Review Github Issues and PR, and associate the milestone of the version + - Run ``make`` to cover the release readiness + - Create a PR to let downstreams developers test their apps and comments + - Create a tag and push tag, it will automatically publish the release to pypi + - Create a release with GitHub Releases + - Merge PR, close Github milestone + + +Initial setup: + - Because we currently use "trusted publisher", it does not require to setup + token. However, OIDC Authorization flow has to be configured in `pypi publishing`. + + - During setup, refer to the environment and name of the workflow directly in the code. + - GitHub Restrictions: tag protection must be enabled + + +.. _`pypi publishing`: https://pypi.org/manage/project/oauthlib/settings/publishing/ diff --git a/oauthlib/__init__.py b/oauthlib/__init__.py index 4f56ef14..2920cf44 100644 --- a/oauthlib/__init__.py +++ b/oauthlib/__init__.py @@ -5,14 +5,14 @@ A generic, spec-compliant, thorough implementation of the OAuth request-signing logic. - :copyright: (c) 2019 by The OAuthlib Community - :license: BSD, see LICENSE for details. + :copyright: (c) The OAuthlib Community + :license: BSD-3-Clause, see LICENSE for details. """ import logging from logging import NullHandler __author__ = 'The OAuthlib Community' -__version__ = '3.2.2' +__version__ = '3.3.0' logging.getLogger('oauthlib').addHandler(NullHandler()) diff --git a/oauthlib/common.py b/oauthlib/common.py index fd9cad09..dfa85179 100644 --- a/oauthlib/common.py +++ b/oauthlib/common.py @@ -198,7 +198,7 @@ def generate_token(length=30, chars=UNICODE_ASCII_CHARACTER_SET): def generate_signed_token(private_pem, request): - import jwt + import jwt # noqa: PLC0415 now = datetime.datetime.utcnow() @@ -216,7 +216,7 @@ def generate_signed_token(private_pem, request): def verify_signed_token(public_pem, token): - import jwt + import jwt # noqa: PLC0415 return jwt.decode(token, public_pem, algorithms=['RS256']) diff --git a/oauthlib/oauth1/rfc5849/signature.py b/oauthlib/oauth1/rfc5849/signature.py index 8916782b..a27cb2e7 100644 --- a/oauthlib/oauth1/rfc5849/signature.py +++ b/oauthlib/oauth1/rfc5849/signature.py @@ -568,7 +568,7 @@ def _get_jwt_rsa_algorithm(hash_algorithm_name: str): # Not in cache: instantiate a new RSAAlgorithm # PyJWT has some nice pycrypto/cryptography abstractions - import jwt.algorithms as jwt_algorithms + import jwt.algorithms as jwt_algorithms # noqa: PLC0415 m = { 'SHA-1': jwt_algorithms.hashes.SHA1, 'SHA-256': jwt_algorithms.hashes.SHA256, diff --git a/oauthlib/oauth2/rfc6749/clients/base.py b/oauthlib/oauth2/rfc6749/clients/base.py index a94e73c4..17f833d2 100644 --- a/oauthlib/oauth2/rfc6749/clients/base.py +++ b/oauthlib/oauth2/rfc6749/clients/base.py @@ -17,6 +17,7 @@ InsecureTransportError, TokenExpiredError, ) from oauthlib.oauth2.rfc6749.parameters import ( + parse_expires, parse_token_response, prepare_token_request, prepare_token_revocation_request, ) @@ -581,15 +582,13 @@ def populate_token_attributes(self, response): if 'token_type' in response: self.token_type = response.get('token_type') - if 'expires_in' in response: - self.expires_in = response.get('expires_in') - self._expires_at = round(time.time()) + int(self.expires_in) - - if 'expires_at' in response: - try: - self._expires_at = round(float(response.get('expires_at'))) - except: - self._expires_at = None + vin, vat, v_at = parse_expires(response) + if vin: + self.expires_in = vin + if vat: + self.expires_at = vat + if v_at: + self._expires_at = v_at if 'mac_key' in response: self.mac_key = response.get('mac_key') diff --git a/oauthlib/oauth2/rfc6749/clients/service_application.py b/oauthlib/oauth2/rfc6749/clients/service_application.py index 8fb17377..abf22d2d 100644 --- a/oauthlib/oauth2/rfc6749/clients/service_application.py +++ b/oauthlib/oauth2/rfc6749/clients/service_application.py @@ -91,7 +91,7 @@ def prepare_request_body(self, ``https://provider.com/oauth2/token``. :param expires_at: A unix expiration timestamp for the JWT. Defaults - to an hour from now, i.e. ``time.time() + 3600``. + to an hour from now, i.e. ``round(time.time()) + 3600``. :param issued_at: A unix timestamp of when the JWT was created. Defaults to now, i.e. ``time.time()``. @@ -149,7 +149,7 @@ def prepare_request_body(self, .. _`Section 3.2.1`: https://tools.ietf.org/html/rfc6749#section-3.2.1 """ - import jwt + import jwt # noqa: PLC0415 key = private_key or self.private_key if not key: diff --git a/oauthlib/oauth2/rfc6749/endpoints/resource.py b/oauthlib/oauth2/rfc6749/endpoints/resource.py index f7562255..d1ff5049 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/resource.py +++ b/oauthlib/oauth2/rfc6749/endpoints/resource.py @@ -81,4 +81,4 @@ def find_token_type(self, request): """ estimates = sorted(((t.estimate_type(request), n) for n, t in self.tokens.items()), reverse=True) - return estimates[0][1] if len(estimates) else None + return estimates[0][1] if estimates else None diff --git a/oauthlib/oauth2/rfc6749/errors.py b/oauthlib/oauth2/rfc6749/errors.py index 3b415748..be8e7a1e 100644 --- a/oauthlib/oauth2/rfc6749/errors.py +++ b/oauthlib/oauth2/rfc6749/errors.py @@ -6,6 +6,8 @@ defined error responses for all four core grant types. """ import json +import inspect +import sys from oauthlib.common import add_params_to_uri, urlencode @@ -386,8 +388,6 @@ def __init__(self, error, *args, **kwargs): def raise_from_error(error, params=None): - import inspect - import sys kwargs = { 'description': params.get('error_description'), 'uri': params.get('error_uri'), diff --git a/oauthlib/oauth2/rfc6749/parameters.py b/oauthlib/oauth2/rfc6749/parameters.py index 6c55000c..4675a31f 100644 --- a/oauthlib/oauth2/rfc6749/parameters.py +++ b/oauthlib/oauth2/rfc6749/parameters.py @@ -336,15 +336,18 @@ def parse_implicit_response(uri, state=None, scope=None): fragment = urlparse.urlparse(uri).fragment params = dict(urlparse.parse_qsl(fragment, keep_blank_values=True)) - for key in ('expires_in',): - if key in params: # cast things to int - params[key] = int(params[key]) - if 'scope' in params: params['scope'] = scope_to_list(params['scope']) - if 'expires_in' in params: - params['expires_at'] = round(time.time()) + int(params['expires_in']) + vin, vat, v_at = parse_expires(params) + if vin: + params['expires_in'] = vin + elif 'expires_in' in params: + params.pop('expires_in') + if vat: + params['expires_at'] = vat + elif 'expires_at' in params: + params.pop('expires_at') if state and params.get('state') != state: raise ValueError("Mismatching or missing state in params.") @@ -423,21 +426,19 @@ def parse_token_response(body, scope=None): # https://github.com/oauthlib/oauthlib/issues/267 params = dict(urlparse.parse_qsl(body)) - for key in ('expires_in',): - if key in params: # cast things to int - params[key] = int(params[key]) if 'scope' in params: params['scope'] = scope_to_list(params['scope']) - if 'expires_in' in params: - if params['expires_in'] is None: - params.pop('expires_in') - else: - params['expires_at'] = time.time() + int(params['expires_in']) - - if isinstance(params.get('expires_at'), float): - params['expires_at'] = round(params['expires_at']) + vin, vat, v_at = parse_expires(params) + if vin: + params['expires_in'] = vin + elif 'expires_in' in params: + params.pop('expires_in') + if vat: + params['expires_at'] = vat + elif 'expires_at' in params: + params.pop('expires_at') params = OAuth2Token(params, old_scope=scope) validate_token_parameters(params) @@ -470,3 +471,56 @@ def validate_token_parameters(params): w.old_scope = params.old_scopes w.new_scope = params.scopes raise w + +def parse_expires(params): + """Parse `expires_in`, `expires_at` fields from params + + Parse following these rules: + - `expires_in` must be either blank or a valid integer. + - `expires_at` is not in specification so it does its best to: + - convert into a int, else + - convert into a float, else + - reuse the same type as-is (usually string) + - `_expires_at` is a special internal value returned to be always an `int`, based + either on the presence of `expires_at`, or reuse the current time plus + `expires_in`. This is typically used to validate token expiry. + + :param params: Dict with expires_in and expires_at optionally set + :return: Tuple of `expires_in`, `expires_at`, and `_expires_at`. None if not set. + """ + expires_in = None + expires_at = None + _expires_at = None + + if 'expires_in' in params: + if isinstance(params.get('expires_in'), int): + expires_in = params.get('expires_in') + elif isinstance(params.get('expires_in'), str): + try: + # Attempt to convert to int + expires_in = int(params.get('expires_in')) + except ValueError: + raise ValueError("expires_int must be an int") + elif params.get('expires_in') is not None: + raise ValueError("expires_int must be an int") + + if 'expires_at' in params: + if isinstance(params.get('expires_at'), (float, int)): + expires_at = params.get('expires_at') + _expires_at = expires_at + elif isinstance(params.get('expires_at'), str): + try: + # Attempt to convert to int first, then float if int fails + expires_at = int(params.get('expires_at')) + _expires_at = expires_at + except ValueError: + try: + expires_at = float(params.get('expires_at')) + _expires_at = expires_at + except ValueError: + # no change from str + expires_at = params.get('expires_at') + if _expires_at is None and expires_in: + expires_at = round(time.time()) + expires_in + _expires_at = expires_at + return expires_in, expires_at, _expires_at diff --git a/oauthlib/openid/connect/core/exceptions.py b/oauthlib/openid/connect/core/exceptions.py index 8a3e79f4..291cf137 100644 --- a/oauthlib/openid/connect/core/exceptions.py +++ b/oauthlib/openid/connect/core/exceptions.py @@ -5,6 +5,9 @@ Error used both by OAuth 2 clients and providers to represent the spec defined error responses for all four core grant types. """ +import inspect +import sys + from oauthlib.oauth2.rfc6749.errors import FatalClientError, OAuth2Error @@ -137,8 +140,6 @@ class InsufficientScopeError(OAuth2Error): def raise_from_error(error, params=None): - import inspect - import sys kwargs = { 'description': params.get('error_description'), 'uri': params.get('error_uri'), diff --git a/requirements-test.txt b/requirements-test.txt index 6d8d6e9d..521036a4 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,3 +1,4 @@ -r requirements.txt pytest>=4.0 pytest-cov>=2.6 +pytest-subtests diff --git a/ruff.toml b/ruff.toml index 0546d652..ec45dd4b 100644 --- a/ruff.toml +++ b/ruff.toml @@ -7,7 +7,7 @@ # start with [tool.ruff # [tool.ruff] -select = [ +lint.select = [ "A", # flake8-builtins "AIR", # Airflow "ASYNC", # flake8-async @@ -65,7 +65,7 @@ select = [ # "TRY", # tryceratops # "UP", # pyupgrade ] -ignore = [ +lint.ignore = [ "F401", "F403", "F405", @@ -88,11 +88,11 @@ line-length = 255 target-version = "py37" # [tool.ruff.mccabe] -[mccabe] +[lint.mccabe] max-complexity = 24 # default is 10 # [tool.ruff.per-file-ignores] -[per-file-ignores] +[lint.per-file-ignores] "docs/conf.py" = ["A001", "INP001"] "oauthlib/oauth2/rfc6749/clients/base.py" = ["E722"] "oauthlib/oauth2/rfc6749/endpoints/base.py" = ["BLE001"] @@ -106,7 +106,7 @@ max-complexity = 24 # default is 10 "oauthlib/openid/connect/core/tokens.py" = ["RUF023"] # [tool.ruff.pylint] -[pylint] +[lint.pylint] allow-magic-value-types = ["int", "str"] max-args = 16 # default is 5 max-branches = 24 # default is 12 diff --git a/setup.py b/setup.py index 1a100bac..70162daa 100755 --- a/setup.py +++ b/setup.py @@ -28,9 +28,8 @@ def fread(fn): long_description=fread('README.rst'), long_description_content_type='text/x-rst', author='The OAuthlib Community', - author_email='idan@gazit.me', - maintainer='Ib Lundgren', - maintainer_email='ib.lundgren@gmail.com', + maintainer='Jonathan Huot', + maintainer_email='jonathan.huot@gmail.com', url='https://github.com/oauthlib/oauthlib', platforms='any', license='BSD-3-Clause', @@ -45,7 +44,6 @@ def fread(fn): 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', 'Operating System :: MacOS', 'Operating System :: POSIX', 'Operating System :: POSIX :: Linux', diff --git a/tests/oauth2/rfc6749/clients/test_base.py b/tests/oauth2/rfc6749/clients/test_base.py index b0b6372b..b0970f2d 100644 --- a/tests/oauth2/rfc6749/clients/test_base.py +++ b/tests/oauth2/rfc6749/clients/test_base.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import datetime +import json from unittest.mock import patch from oauthlib import common @@ -303,31 +304,6 @@ def test_prepare_refresh_token_request(self): self.assertEqual(h, {'Content-Type': 'application/x-www-form-urlencoded'}) self.assertFormBodyEqual(b, 'grant_type=refresh_token&scope={}&refresh_token={}'.format(scope, token)) - def test_parse_token_response_invalid_expires_at(self): - token_json = ('{ "access_token":"2YotnFZFEjr1zCsicMWpAA",' - ' "token_type":"example",' - ' "expires_at":"2006-01-02T15:04:05Z",' - ' "scope":"/profile",' - ' "example_parameter":"example_value"}') - token = { - "access_token": "2YotnFZFEjr1zCsicMWpAA", - "token_type": "example", - "expires_at": "2006-01-02T15:04:05Z", - "scope": ["/profile"], - "example_parameter": "example_value" - } - - client = Client(self.client_id) - - # Parse code and state - response = client.parse_request_body_response(token_json, scope=["/profile"]) - self.assertEqual(response, token) - self.assertEqual(None, client._expires_at) - self.assertEqual(client.access_token, response.get("access_token")) - self.assertEqual(client.refresh_token, response.get("refresh_token")) - self.assertEqual(client.token_type, response.get("token_type")) - - def test_create_code_verifier_min_length(self): client = Client(self.client_id) length = 43 @@ -361,20 +337,28 @@ def test_create_code_challenge_s256(self): code_challenge_s256 = client.create_code_challenge(code_verifier=code_verifier, code_challenge_method='S256') self.assertEqual(code_challenge_s256, client.code_challenge) - def test_parse_token_response_expires_at_is_int(self): - expected_expires_at = 1661185149 - token_json = ('{ "access_token":"2YotnFZFEjr1zCsicMWpAA",' - ' "token_type":"example",' - ' "expires_at":1661185148.6437678,' - ' "scope":"/profile",' - ' "example_parameter":"example_value"}') - - client = Client(self.client_id) - - response = client.parse_request_body_response(token_json, scope=["/profile"]) - - self.assertEqual(response['expires_at'], expected_expires_at) - self.assertEqual(client._expires_at, expected_expires_at) + def test_parse_token_response_expires_at_types(self): + for title, fieldjson, expected, generated in [ + ('int', 1661185148, 1661185148, 1661185148), + ('float', 1661185148.6437678, 1661185148.6437678, 1661185148.6437678), + ('str', "\"2006-01-02T15:04:05Z\"", "2006-01-02T15:04:05Z", None), + ('str-as-int', "\"1661185148\"", 1661185148, 1661185148), + ('str-as-float', "\"1661185148.42\"", 1661185148.42, 1661185148.42), + ]: + with self.subTest(msg=title): + token_json = ('{{ "access_token":"2YotnFZFEjr1zCsicMWpAA",' + ' "token_type":"example",' + ' "expires_at":{expires_at},' + ' "scope":"/profile",' + ' "example_parameter":"example_value"}}'.format(expires_at=fieldjson)) + + client = Client(self.client_id) + response = client.parse_request_body_response(token_json, scope=["/profile"]) + + self.assertEqual(response['expires_at'], expected, "response attribute wrong") + self.assertEqual(client.expires_at, expected, "client attribute wrong") + if generated: + self.assertEqual(client._expires_at, generated, "internal expiration wrong") @patch('time.time') def test_parse_token_response_generated_expires_at_is_int(self, t): diff --git a/tests/oauth2/rfc6749/endpoints/test_metadata.py b/tests/oauth2/rfc6749/endpoints/test_metadata.py index facf69d0..c36d94bf 100644 --- a/tests/oauth2/rfc6749/endpoints/test_metadata.py +++ b/tests/oauth2/rfc6749/endpoints/test_metadata.py @@ -20,8 +20,8 @@ def test_openid_oauth2_preconfigured(self): "introspection_endpoint": "https://foo.bar/introspect", "token_endpoint": "https://foo.bar/token" } - from oauthlib.oauth2 import Server as OAuth2Server - from oauthlib.openid import Server as OpenIDServer + from oauthlib.oauth2 import Server as OAuth2Server # noqa: PLC0415 + from oauthlib.openid import Server as OpenIDServer # noqa: PLC0415 endpoint = OAuth2Server(None) metadata = MetadataEndpoint([endpoint], default_claims) diff --git a/tests/oauth2/rfc6749/grant_types/test_refresh_token.py b/tests/oauth2/rfc6749/grant_types/test_refresh_token.py index 0a4ddd9a..f963444a 100644 --- a/tests/oauth2/rfc6749/grant_types/test_refresh_token.py +++ b/tests/oauth2/rfc6749/grant_types/test_refresh_token.py @@ -184,7 +184,7 @@ def test_valid_token_request(self): # all ok but without request.scope del self.request.scope self.auth.validate_token_request(self.request) - self.assertEqual(self.request.scopes, 'foo bar baz'.split()) + self.assertEqual(self.request.scopes, ['foo', 'bar', 'baz']) # CORS diff --git a/tests/oauth2/rfc6749/test_parameters.py b/tests/oauth2/rfc6749/test_parameters.py index cd8c9e95..63b74c37 100644 --- a/tests/oauth2/rfc6749/test_parameters.py +++ b/tests/oauth2/rfc6749/test_parameters.py @@ -302,3 +302,25 @@ def record_scope_change(sender, message, old, new): finally: signals.scope_changed.disconnect(record_scope_change) del os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] + + + def test_parse_expires(self): + for title, arg, expected in [ + ('none', (None, None), (None, None, None)), + ('expires_in only', (3600, None), (3600, 4600, 4600)), + ('expires_in and expires_at', (3600, 200), (3600, 200, 200)), + ('expires_in and expires_at float', (3600, 200.42), (3600, 200.42, 200.42)), + ('expires_in and expires_at str-int', (3600, "200"), (3600, 200, 200)), + ('expires_in and expires_at str-float', (3600, "200.42"), (3600, 200.42, 200.42)), + ('expires_in str only', ("3600", None), (3600, 4600, 4600)), + ('expires_in str and expires_at', ("3600", 200), (3600, 200, 200)), + ('expires_in str and expires_at float', ("3600", 200.42), (3600, 200.42, 200.42)), + ('expires_in str and expires_at str-int', ("3600", "200"), (3600, 200, 200)), + ('expires_in str and expires_at str-float', ("3600", "200.42"), (3600, 200.42, 200.42)), + ]: + with self.subTest(msg=title): + params = { + "expires_in": arg[0], + "expires_at": arg[1] + } + self.assertEqual(expected, parse_expires(params)) diff --git a/tests/test_uri_validate.py b/tests/test_uri_validate.py index 04138d60..f1ac404e 100644 --- a/tests/test_uri_validate.py +++ b/tests/test_uri_validate.py @@ -1,3 +1,4 @@ +from datetime import datetime import unittest from oauthlib.uri_validate import is_absolute_uri @@ -77,7 +78,6 @@ def test_failures(self): self.assertIsNone(is_absolute_uri('http://[abcd:efgh::1]/')) def test_recursive_regex(self): - from datetime import datetime t0 = datetime.now() is_absolute_uri('http://[::::::::::::::::::::::::::]/path') t1 = datetime.now() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy