fastapi-commons 0.8.2__tar.gz → 0.8.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/.pre-commit-config.yaml +2 -2
- {fastapi_commons-0.8.2/src/fastapi_commons.egg-info → fastapi_commons-0.8.4}/PKG-INFO +4 -3
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/pyproject.toml +3 -2
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons/auth.py +29 -2
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4/src/fastapi_commons.egg-info}/PKG-INFO +4 -3
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons.egg-info/SOURCES.txt +1 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons.egg-info/requires.txt +3 -2
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons.egg-info/scm_file_list.json +1 -0
- fastapi_commons-0.8.4/src/fastapi_commons.egg-info/scm_version.json +8 -0
- fastapi_commons-0.8.4/tests/unit/test_auth.py +55 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/uv.lock +219 -196
- fastapi_commons-0.8.2/src/fastapi_commons.egg-info/scm_version.json +0 -8
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/.coveragerc +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/.env_template +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/.github/workflows/checks.yml +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/.github/workflows/python-publish.yaml +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/.github/workflows/release-on-tag-push.yml +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/.gitignore +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/.python-version +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/AUTHORS.rst +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/CHANGELOG.rst +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/LICENSE +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/README.md +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/README.rst +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/setup.cfg +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons/__init__.py +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons/conf.py +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons/handlers.py +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons/helpers.py +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons/instrumentation.py +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons/log/__init__.py +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons/log/filters.py +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons/middleware/__init__.py +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons/middleware/correlation_id.py +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons/middleware/log_context.py +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons/middleware/prometheus.py +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons.egg-info/dependency_links.txt +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/src/fastapi_commons.egg-info/top_level.txt +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/tests/__init__.py +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/tests/conftest.py +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/tests/unit/__init__.py +0 -0
- {fastapi_commons-0.8.2 → fastapi_commons-0.8.4}/tests/unit/test_poc.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
repos:
|
|
2
2
|
- repo: https://github.com/astral-sh/uv-pre-commit
|
|
3
|
-
rev: 0.12.
|
|
3
|
+
rev: 0.12.5
|
|
4
4
|
hooks:
|
|
5
5
|
- id: uv-lock
|
|
6
6
|
# - id: uv-export
|
|
7
7
|
|
|
8
8
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
9
|
-
rev: v0.16.
|
|
9
|
+
rev: v0.16.4
|
|
10
10
|
hooks:
|
|
11
11
|
# Run the linter.
|
|
12
12
|
- id: ruff-check
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-commons
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
4
4
|
Summary: Re-usable FastAPI code
|
|
5
5
|
Author-email: Oleg Korsak <kamikaze.is.waiting.you@gmail.com>
|
|
6
6
|
License-Expression: GPL-3.0
|
|
@@ -19,14 +19,15 @@ Requires-Dist: msgspec~=0.21.1
|
|
|
19
19
|
Requires-Dist: pydantic[email]~=2.13.4
|
|
20
20
|
Requires-Dist: pydantic-settings~=2.15.0
|
|
21
21
|
Requires-Dist: python-jose~=3.5.0
|
|
22
|
-
Requires-Dist: SQLAlchemy[asyncio]~=2.0.
|
|
22
|
+
Requires-Dist: SQLAlchemy[asyncio]~=2.0.52
|
|
23
23
|
Requires-Dist: opentelemetry-api~=1.44.0
|
|
24
24
|
Requires-Dist: opentelemetry-sdk~=1.44.0
|
|
25
25
|
Requires-Dist: opentelemetry-exporter-otlp~=1.44.0
|
|
26
26
|
Requires-Dist: opentelemetry-instrumentation-fastapi~=0.65b0
|
|
27
27
|
Requires-Dist: opentelemetry-instrumentation-logging~=0.65b0
|
|
28
28
|
Requires-Dist: prometheus-client~=0.26.0
|
|
29
|
-
Requires-Dist: python3-commons[authn]~=0.24.
|
|
29
|
+
Requires-Dist: python3-commons[authn]~=0.24.5
|
|
30
|
+
Requires-Dist: starlette~=1.6.0
|
|
30
31
|
Dynamic: license-file
|
|
31
32
|
|
|
32
33
|
FastAPI commons
|
|
@@ -25,14 +25,15 @@ dependencies = [
|
|
|
25
25
|
"pydantic[email]~=2.13.4",
|
|
26
26
|
"pydantic-settings~=2.15.0",
|
|
27
27
|
"python-jose~=3.5.0",
|
|
28
|
-
"SQLAlchemy[asyncio]~=2.0.
|
|
28
|
+
"SQLAlchemy[asyncio]~=2.0.52",
|
|
29
29
|
"opentelemetry-api~=1.44.0",
|
|
30
30
|
"opentelemetry-sdk~=1.44.0",
|
|
31
31
|
"opentelemetry-exporter-otlp~=1.44.0",
|
|
32
32
|
"opentelemetry-instrumentation-fastapi~=0.65b0",
|
|
33
33
|
"opentelemetry-instrumentation-logging~=0.65b0",
|
|
34
34
|
"prometheus-client~=0.26.0",
|
|
35
|
-
"python3-commons[authn]~=0.24.
|
|
35
|
+
"python3-commons[authn]~=0.24.5",
|
|
36
|
+
"starlette~=1.6.0",
|
|
36
37
|
]
|
|
37
38
|
|
|
38
39
|
[dependency-groups]
|
|
@@ -5,9 +5,13 @@ from typing import Annotated, Any, TypeVar
|
|
|
5
5
|
|
|
6
6
|
import msgspec
|
|
7
7
|
from fastapi import Depends, HTTPException
|
|
8
|
-
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
8
|
+
from fastapi.security import APIKeyHeader, HTTPAuthorizationCredentials, HTTPBearer
|
|
9
9
|
from jose import ExpiredSignatureError, JWSError, JWTError, jwt
|
|
10
10
|
from python3_commons.auth import OIDCClient, TokenData
|
|
11
|
+
from python3_commons.db.models.auth import ApiKey
|
|
12
|
+
from sqlalchemy import select
|
|
13
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
14
|
+
from starlette import status
|
|
11
15
|
|
|
12
16
|
from fastapi_commons.conf import api_auth_settings, oidc_settings
|
|
13
17
|
|
|
@@ -17,7 +21,7 @@ bearer_security = HTTPBearer(auto_error=api_auth_settings.enabled)
|
|
|
17
21
|
oidc_client = OIDCClient(
|
|
18
22
|
oidc_settings.authority_url,
|
|
19
23
|
oidc_settings.client_id,
|
|
20
|
-
oidc_settings.client_secret,
|
|
24
|
+
oidc_settings.client_secret.get_secret_value(),
|
|
21
25
|
timeout=oidc_settings.timeout,
|
|
22
26
|
verify_cert=oidc_settings.verify_cert,
|
|
23
27
|
connection_limit=oidc_settings.connection_limit,
|
|
@@ -77,3 +81,26 @@ def get_token_verifier[T](
|
|
|
77
81
|
return token_data
|
|
78
82
|
|
|
79
83
|
return get_verified_token
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def get_api_key_verifier(
|
|
87
|
+
db_dependency: Callable[..., AsyncSession], header_name: str = 'Api-Key'
|
|
88
|
+
) -> Callable[..., Coroutine[Any, Any, ApiKey]]:
|
|
89
|
+
async def verify_api_key(
|
|
90
|
+
api_key: Annotated[str, Depends(APIKeyHeader(name=header_name, auto_error=True))],
|
|
91
|
+
session: Annotated[AsyncSession, Depends(db_dependency)],
|
|
92
|
+
) -> ApiKey:
|
|
93
|
+
stmt = select(ApiKey).where(ApiKey.uid == api_key)
|
|
94
|
+
result = await session.execute(stmt)
|
|
95
|
+
api_key_obj = result.scalar_one_or_none()
|
|
96
|
+
|
|
97
|
+
if not api_key_obj:
|
|
98
|
+
raise HTTPException(
|
|
99
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
100
|
+
detail='Invalid API Key',
|
|
101
|
+
headers={'WWW-Authenticate': 'Api-Key'},
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return api_key_obj
|
|
105
|
+
|
|
106
|
+
return verify_api_key
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-commons
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
4
4
|
Summary: Re-usable FastAPI code
|
|
5
5
|
Author-email: Oleg Korsak <kamikaze.is.waiting.you@gmail.com>
|
|
6
6
|
License-Expression: GPL-3.0
|
|
@@ -19,14 +19,15 @@ Requires-Dist: msgspec~=0.21.1
|
|
|
19
19
|
Requires-Dist: pydantic[email]~=2.13.4
|
|
20
20
|
Requires-Dist: pydantic-settings~=2.15.0
|
|
21
21
|
Requires-Dist: python-jose~=3.5.0
|
|
22
|
-
Requires-Dist: SQLAlchemy[asyncio]~=2.0.
|
|
22
|
+
Requires-Dist: SQLAlchemy[asyncio]~=2.0.52
|
|
23
23
|
Requires-Dist: opentelemetry-api~=1.44.0
|
|
24
24
|
Requires-Dist: opentelemetry-sdk~=1.44.0
|
|
25
25
|
Requires-Dist: opentelemetry-exporter-otlp~=1.44.0
|
|
26
26
|
Requires-Dist: opentelemetry-instrumentation-fastapi~=0.65b0
|
|
27
27
|
Requires-Dist: opentelemetry-instrumentation-logging~=0.65b0
|
|
28
28
|
Requires-Dist: prometheus-client~=0.26.0
|
|
29
|
-
Requires-Dist: python3-commons[authn]~=0.24.
|
|
29
|
+
Requires-Dist: python3-commons[authn]~=0.24.5
|
|
30
|
+
Requires-Dist: starlette~=1.6.0
|
|
30
31
|
Dynamic: license-file
|
|
31
32
|
|
|
32
33
|
FastAPI commons
|
|
@@ -5,11 +5,12 @@ msgspec~=0.21.1
|
|
|
5
5
|
pydantic[email]~=2.13.4
|
|
6
6
|
pydantic-settings~=2.15.0
|
|
7
7
|
python-jose~=3.5.0
|
|
8
|
-
SQLAlchemy[asyncio]~=2.0.
|
|
8
|
+
SQLAlchemy[asyncio]~=2.0.52
|
|
9
9
|
opentelemetry-api~=1.44.0
|
|
10
10
|
opentelemetry-sdk~=1.44.0
|
|
11
11
|
opentelemetry-exporter-otlp~=1.44.0
|
|
12
12
|
opentelemetry-instrumentation-fastapi~=0.65b0
|
|
13
13
|
opentelemetry-instrumentation-logging~=0.65b0
|
|
14
14
|
prometheus-client~=0.26.0
|
|
15
|
-
python3-commons[authn]~=0.24.
|
|
15
|
+
python3-commons[authn]~=0.24.5
|
|
16
|
+
starlette~=1.6.0
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
from typing import Annotated
|
|
3
|
+
from unittest.mock import AsyncMock, MagicMock
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from fastapi import HTTPException
|
|
7
|
+
from python3_commons.db.models.auth import ApiKey
|
|
8
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
9
|
+
|
|
10
|
+
from fastapi_commons.auth import get_api_key_verifier
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.mark.asyncio
|
|
14
|
+
async def test_create_api_key_verifier_success() -> None:
|
|
15
|
+
# Mock database session
|
|
16
|
+
mock_session = AsyncMock(spec=AsyncSession)
|
|
17
|
+
mock_api_key = MagicMock(spec=ApiKey)
|
|
18
|
+
mock_api_key.uid = 'test-key'
|
|
19
|
+
|
|
20
|
+
# Mock result
|
|
21
|
+
mock_result = MagicMock()
|
|
22
|
+
mock_result.scalar_one_or_none.return_value = mock_api_key
|
|
23
|
+
mock_session.execute.return_value = mock_result
|
|
24
|
+
|
|
25
|
+
# Create verifier
|
|
26
|
+
# We pass a mock dependency. For direct calling, we just need Any.
|
|
27
|
+
verifier = get_api_key_verifier(lambda: mock_session)
|
|
28
|
+
|
|
29
|
+
# Test the verifier directly
|
|
30
|
+
result = await verifier(api_key='test-key', session=mock_session)
|
|
31
|
+
assert result == mock_api_key
|
|
32
|
+
|
|
33
|
+
# Verify DB call
|
|
34
|
+
mock_session.execute.assert_called_once()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@pytest.mark.asyncio
|
|
38
|
+
async def test_create_api_key_verifier_fail() -> None:
|
|
39
|
+
# Mock database session
|
|
40
|
+
mock_session = AsyncMock(spec=AsyncSession)
|
|
41
|
+
|
|
42
|
+
# Mock result to return None
|
|
43
|
+
mock_result = MagicMock()
|
|
44
|
+
mock_result.scalar_one_or_none.return_value = None
|
|
45
|
+
mock_session.execute.return_value = mock_result
|
|
46
|
+
|
|
47
|
+
# Create verifier
|
|
48
|
+
verifier = get_api_key_verifier(lambda: mock_session)
|
|
49
|
+
|
|
50
|
+
# Test the verifier directly and expect HTTPException
|
|
51
|
+
with pytest.raises(HTTPException) as excinfo:
|
|
52
|
+
await verifier(api_key='invalid-key', session=mock_session)
|
|
53
|
+
|
|
54
|
+
assert excinfo.value.status_code == HTTPStatus.UNAUTHORIZED
|
|
55
|
+
assert excinfo.value.detail == 'Invalid API Key'
|