diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 80f936e..78d7197 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.3.0 +current_version = 2.4.0 commit = False tag = False parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+)(?P\d+))? diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml index f13b7b1..508a9ff 100644 --- a/.github/workflows/test-package.yml +++ b/.github/workflows/test-package.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.11', '3.12'] + python-version: ['3.11', '3.12', '3.13'] fail-fast: false steps: - uses: actions/checkout@v2 @@ -25,18 +25,13 @@ jobs: python -m pip install --upgrade pip pip install flit flit install --deps develop - - name: Check formatting - run: | - black --check . - - name: Lint with ruff - run: | - ruff check . - - name: MyPy - run: | - mypy -p oauth2_lib + - name: Mypy + run: mypy . - name: License headers run: | apache-license-check --copyright "2019-`date +%Y` SURF" oauth2_lib + - name: Run pre-commit hooks + uses: pre-commit/action@v3.0.1 - name: Test with pytest run: | mkdir reports diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d960a47..8a74ba7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,23 +1,22 @@ repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.16.0 + rev: v3.19.1 hooks: - id: pyupgrade args: - --py311-plus - --keep-runtime-typing - repo: https://github.com/psf/black - rev: 24.4.2 + rev: 24.10.0 hooks: - id: black - language_version: python3.11 - repo: https://github.com/asottile/blacken-docs - rev: 1.18.0 + rev: 1.19.1 hooks: - id: blacken-docs - additional_dependencies: [black==22.10.0] + additional_dependencies: [black==24.10.0] - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: trailing-whitespace exclude: .bumpversion.cfg @@ -31,20 +30,10 @@ repos: - id: detect-private-key - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.5.1 + rev: v0.8.4 hooks: - id: ruff args: [ --fix, --exit-non-zero-on-fix, --show-fixes ] - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.1 - hooks: - - id: mypy - language_version: python3.11 - additional_dependencies: [pydantic<2.0.0, strawberry-graphql] - args: - - --no-warn-unused-ignores - - --allow-untyped-decorators - exclude: (test/*|migrations/*) - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: @@ -56,7 +45,3 @@ repos: rev: v0.10.0.1 hooks: - id: shellcheck - - repo: https://github.com/andreoliwa/nitpick - rev: v0.35.0 - hooks: - - id: nitpick diff --git a/README.md b/README.md index 69dbd73..a645f01 100644 --- a/README.md +++ b/README.md @@ -4,42 +4,93 @@ [![Supported python versions](https://img.shields.io/pypi/pyversions/oauth2-lib.svg?color=%2334D058)](https://pypi.org/project/oauth2-lib) [![codecov](https://codecov.io/gh/workfloworchestrator/oauth2-lib/graph/badge.svg?token=JDMMBBOVM4)](https://codecov.io/gh/workfloworchestrator/oauth2-lib) -This Project contains a Mixin class that wraps an openapi-codegen python client, to inject Opentelemetry spans -and api call retries. It also contains a number of FastAPI dependencies which enables Policy enforcement offloading -to Open Policy Agent. +This project contains a number of classes to perform authentication (AuthN) and authorization (AuthZ) in a FastAPI application. -The project contains a number of OIDC classes that are tailored to the SURF environment. +They can be found in [oauth2_lib/fastapi.py](oauth2_lib/fastapi.py). +Most notable are: +- `OIDCAuth`: AuthN implementation that authenticates a user against a OIDC backend. You can subclass and implement `def userinfo()` as needed. + - To use a different AuthN method, subclass the `Authentication` base class. +- `OIDCUserModel`: model of the data returned by `OIDCAuth`. You can subclass this to rename and/or add fields. +- `OPAAuthorization`: AuthZ implementation that authorizes a user's HTTP request against an Open Policy Agent (OPA) instance. + - To use a different AuthZ method, subclass the `Authorization` base class. +- `GraphQLOPAAuthorization`: AuthZ implementation that authorizes a user's GraphQL query against an Open Policy Agent (OPA) instance. + - To use a different AuthZ method, subclass the `GraphqlAuthorization` base class. +- `OPAResult`: model of the data returned by `OPAAuthorization` and `GraphQLOPAAuthorization`. +The [orchestrator-core documentation](https://workfloworchestrator.org/orchestrator-core) has a section on Authentication and Authorization that describes how to use/override these classes. ## Installation -This can be done as follows: + +To install the package from PyPI: + +```bash +pip install oauth2-lib +``` + +## Development + +### Virtual Environment + +Steps to setup a virtual environment. #### Step 1: -First install flit to enable you to develop on this repository + +Create and activate a python3 virtualenv. + +#### Step 2: + +Install flit to enable you to develop on this repository: + ```bash pip install flit ``` -#### Step 2: -To install all development dependencies +#### Step 3: + +To install all development dependencies: + ```bash -flit install --deps develop --symlink +flit install --deps develop ``` -for pydantic V2 you also need to install pydantic_settings: `pip install pydantic_settings`. +All steps combined into 1 command: -This way all requirements are installed for testing and development. +```bash +python -m venv .venv && source .venv/bin/activate && pip install -U pip && pip install flit && flit install --deps develop +``` + +### Unit tests + +Activate the virtualenv and run the unit tests with: + +```bash +pytest +``` + +### Pre-commit + +This project uses [pre-commit](https://pre-commit.com/) to automatically run a number of checks before making a git commit. +The same checks will be performed in the CI pipeline so this can save you some time. + +First ensure you have pre-commit installed. +It is recommended to install it outside the virtualenv. +On Linux and Mac, pre-commit is available in most package managers. Alternatively you can install it globally with [pipx](https://github.com/pypa/pipx). + +Once pre-commit is installed, go into the project root and enable it: +```bash +pre-commit install +``` + +This should output `pre-commit installed at .git/hooks/pre-commit`. The next time you run `git commit` the pre-commit hooks will validate your changes. + +### Bump version -## Development Depending on the feature type, run bumpversion (patch|minor|major) to increment the version you are working on. For example to update the increment the patch version use ```bash bumpversion patch ``` -## For MAC users looking and experimenting with Opentelemetry (OTEL) -https://github.com/jaegertracing/jaeger-client-node/issues/124#issuecomment-324222456 - ## Supported Python versions oauth2-lib must support the same python versions as [orchestrator-core](https://github.com/workfloworchestrator/orchestrator-core). diff --git a/oauth2_lib/__init__.py b/oauth2_lib/__init__.py index c2c9811..8a2ce2f 100644 --- a/oauth2_lib/__init__.py +++ b/oauth2_lib/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2019-2024 SURF. +# Copyright 2019-2025 SURF. # 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 @@ -13,4 +13,4 @@ """This is the SURF Oauth2 module that interfaces with the oauth2 setup.""" -__version__ = "2.3.0" +__version__ = "2.4.0" diff --git a/oauth2_lib/async_api_client.py b/oauth2_lib/async_api_client.py index 02698f5..3923f65 100644 --- a/oauth2_lib/async_api_client.py +++ b/oauth2_lib/async_api_client.py @@ -1,4 +1,4 @@ -# Copyright 2019-2024 SURF. +# Copyright 2019-2025 SURF. # 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 diff --git a/oauth2_lib/fastapi.py b/oauth2_lib/fastapi.py index 42b4c8e..9170cbb 100644 --- a/oauth2_lib/fastapi.py +++ b/oauth2_lib/fastapi.py @@ -1,4 +1,4 @@ -# Copyright 2019-2024 SURF. +# Copyright 2019-2025 SURF. # 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 @@ -151,7 +151,7 @@ class HttpBearerExtractor(HTTPBearer, IdTokenExtractor): def __init__(self, auto_error: bool = False): super().__init__(auto_error=auto_error) - async def __call__(self, request: Request) -> Optional[HTTPAuthorizationCredentials]: + async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None: """Extract the Authorization header from the request.""" return await super().__call__(request) diff --git a/oauth2_lib/settings.py b/oauth2_lib/settings.py index 587a86d..e6c0edc 100644 --- a/oauth2_lib/settings.py +++ b/oauth2_lib/settings.py @@ -1,4 +1,4 @@ -# Copyright 2019-2024 SURF. +# Copyright 2019-2025 SURF. # 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 diff --git a/oauth2_lib/strawberry.py b/oauth2_lib/strawberry.py index b72d557..dd346c4 100644 --- a/oauth2_lib/strawberry.py +++ b/oauth2_lib/strawberry.py @@ -1,4 +1,4 @@ -# Copyright 2019-2024 SURF. +# Copyright 2019-2025 SURF. # 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 diff --git a/pyproject.toml b/pyproject.toml index c4199b0..45f5fab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ classifiers = [ "Intended Audience :: Telecommunications Industry", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.11", ] @@ -40,7 +41,7 @@ requires = [ "asyncstdlib", ] description-file = "README.md" -requires-python = ">=3.11,<3.13" +requires-python = ">=3.11,<3.14" [tool.flit.metadata.urls] Documentation = "https://workfloworchestrator.org/" @@ -68,7 +69,7 @@ skip = ["src", "venv"] [tool.black] line-length = 120 -target-version = ["py310"] +target-version = ["py311"] exclude = ''' ( /( diff --git a/tests/strawberry/conftest.py b/tests/strawberry/conftest.py index 3b5e191..5ff0a04 100644 --- a/tests/strawberry/conftest.py +++ b/tests/strawberry/conftest.py @@ -77,7 +77,7 @@ async def get_context(auth_manager=Depends(get_auth_manger)) -> OauthContext: # app = FastAPI() schema = strawberry.Schema(query=Query, mutation=Mutation) - graphql_app: GraphQLRouter = GraphQLRouter(schema, context_getter=get_context) + graphql_app: GraphQLRouter = GraphQLRouter(schema, context_getter=get_context) # type: ignore[arg-type] app.include_router(graphql_app, prefix="/graphql") return TestClient(app)