usso 0.28.0__tar.gz → 0.28.1__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.
Files changed (35) hide show
  1. {usso-0.28.0/src/usso.egg-info → usso-0.28.1}/PKG-INFO +1 -1
  2. {usso-0.28.0 → usso-0.28.1}/pyproject.toml +1 -1
  3. {usso-0.28.0 → usso-0.28.1}/src/usso/auth/client.py +6 -4
  4. usso-0.28.1/src/usso/integrations/fastapi/__init__.py +8 -0
  5. {usso-0.28.0 → usso-0.28.1}/src/usso/integrations/fastapi/dependency.py +17 -32
  6. usso-0.28.1/src/usso/integrations/fastapi/handler.py +16 -0
  7. {usso-0.28.0 → usso-0.28.1/src/usso.egg-info}/PKG-INFO +1 -1
  8. {usso-0.28.0 → usso-0.28.1}/src/usso.egg-info/SOURCES.txt +1 -0
  9. usso-0.28.1/tests/test_fastapi.py +102 -0
  10. usso-0.28.0/src/usso/integrations/fastapi/__init__.py +0 -3
  11. usso-0.28.0/tests/test_fastapi.py +0 -79
  12. {usso-0.28.0 → usso-0.28.1}/LICENSE.txt +0 -0
  13. {usso-0.28.0 → usso-0.28.1}/MANIFEST.in +0 -0
  14. {usso-0.28.0 → usso-0.28.1}/README.md +0 -0
  15. {usso-0.28.0 → usso-0.28.1}/pytest.ini +0 -0
  16. {usso-0.28.0 → usso-0.28.1}/setup.cfg +0 -0
  17. {usso-0.28.0 → usso-0.28.1}/src/usso/__init__.py +0 -0
  18. {usso-0.28.0 → usso-0.28.1}/src/usso/auth/__init__.py +0 -0
  19. {usso-0.28.0 → usso-0.28.1}/src/usso/auth/api_key.py +0 -0
  20. {usso-0.28.0 → usso-0.28.1}/src/usso/auth/config.py +0 -0
  21. {usso-0.28.0 → usso-0.28.1}/src/usso/exceptions.py +0 -0
  22. {usso-0.28.0 → usso-0.28.1}/src/usso/integrations/django/__init__.py +0 -0
  23. {usso-0.28.0 → usso-0.28.1}/src/usso/integrations/django/middleware.py +0 -0
  24. {usso-0.28.0 → usso-0.28.1}/src/usso/models/user.py +0 -0
  25. {usso-0.28.0 → usso-0.28.1}/src/usso/session/__init__.py +0 -0
  26. {usso-0.28.0 → usso-0.28.1}/src/usso/session/async_session.py +0 -0
  27. {usso-0.28.0 → usso-0.28.1}/src/usso/session/base_session.py +0 -0
  28. {usso-0.28.0 → usso-0.28.1}/src/usso/session/session.py +0 -0
  29. {usso-0.28.0 → usso-0.28.1}/src/usso/utils/__init__.py +0 -0
  30. {usso-0.28.0 → usso-0.28.1}/src/usso/utils/method_utils.py +0 -0
  31. {usso-0.28.0 → usso-0.28.1}/src/usso/utils/string_utils.py +0 -0
  32. {usso-0.28.0 → usso-0.28.1}/src/usso.egg-info/dependency_links.txt +0 -0
  33. {usso-0.28.0 → usso-0.28.1}/src/usso.egg-info/entry_points.txt +0 -0
  34. {usso-0.28.0 → usso-0.28.1}/src/usso.egg-info/requires.txt +0 -0
  35. {usso-0.28.0 → usso-0.28.1}/src/usso.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: usso
3
- Version: 0.28.0
3
+ Version: 0.28.1
4
4
  Summary: A plug-and-play client for integrating universal single sign-on (SSO) with Python frameworks, enabling secure and seamless authentication across microservices.
5
5
  Author-email: Mahdi Kiani <mahdikiany@gmail.com>
6
6
  Maintainer-email: Mahdi Kiani <mahdikiany@gmail.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "usso"
7
- version = "0.28.0"
7
+ version = "0.28.1"
8
8
  description = "A plug-and-play client for integrating universal single sign-on (SSO) with Python frameworks, enabling secure and seamless authentication across microservices."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -65,10 +65,12 @@ class UssoAuth:
65
65
  except usso_jwt.exceptions.JWTError as e:
66
66
  exp = e
67
67
 
68
- if raise_exception:
69
- if exp:
70
- _handle_exception("unauthorized", message=str(exp), **kwargs)
71
- _handle_exception("unauthorized", **kwargs)
68
+ _handle_exception(
69
+ "Unauthorized",
70
+ message=str(exp) if exp else None,
71
+ raise_exception=raise_exception,
72
+ **kwargs,
73
+ )
72
74
 
73
75
  def user_data_from_api_key(self, api_key: str) -> UserData:
74
76
  """Get user data from an API key.
@@ -0,0 +1,8 @@
1
+ from .dependency import USSOAuthentication
2
+ from .handler import EXCEPTION_HANDLERS, usso_exception_handler
3
+
4
+ __all__ = [
5
+ "USSOAuthentication",
6
+ "EXCEPTION_HANDLERS",
7
+ "usso_exception_handler",
8
+ ]
@@ -1,13 +1,10 @@
1
1
  import logging
2
2
 
3
- from starlette.status import HTTP_401_UNAUTHORIZED
4
-
5
3
  from fastapi import Request, WebSocket
6
- from fastapi.responses import JSONResponse
7
4
 
8
5
  from ...auth import UssoAuth
9
6
  from ...auth.config import AuthConfig, AvailableJwtConfigs
10
- from ...exceptions import USSOException
7
+ from ...exceptions import _handle_exception
11
8
  from ...models.user import UserData
12
9
  from ...utils.method_utils import instance_method
13
10
 
@@ -26,6 +23,9 @@ class USSOAuthentication(UssoAuth):
26
23
  super().__init__(jwt_config=jwt_config)
27
24
  self.raise_exception = raise_exception
28
25
 
26
+ def __call__(self, request: Request) -> UserData:
27
+ return self.usso_access_security(request)
28
+
29
29
  @instance_method
30
30
  def get_request_jwt(self, request: Request | WebSocket) -> str | None:
31
31
  for jwt_config in self.jwt_configs:
@@ -42,7 +42,7 @@ class USSOAuthentication(UssoAuth):
42
42
  return token
43
43
  return None
44
44
 
45
- @instance_method
45
+ # @instance_method
46
46
  def usso_access_security(self, request: Request) -> UserData | None:
47
47
  """Return the user associated with a token value."""
48
48
  api_key = self.get_request_api_key(request)
@@ -54,15 +54,14 @@ class USSOAuthentication(UssoAuth):
54
54
  return self.user_data_from_token(
55
55
  token, raise_exception=self.raise_exception
56
56
  )
57
- if self.raise_exception:
58
- raise USSOException(
59
- status_code=HTTP_401_UNAUTHORIZED,
60
- error="unauthorized",
61
- message="No token provided",
62
- )
63
- return None
64
57
 
65
- @instance_method
58
+ _handle_exception(
59
+ "Unauthorized",
60
+ message="No token provided",
61
+ raise_exception=self.raise_exception,
62
+ )
63
+
64
+ # @instance_method
66
65
  def jwt_access_security_ws(self, websocket: WebSocket) -> UserData | None:
67
66
  """Return the user associated with a token value."""
68
67
  api_key = self.get_request_api_key(websocket)
@@ -74,22 +73,8 @@ class USSOAuthentication(UssoAuth):
74
73
  return self.user_data_from_token(
75
74
  token, raise_exception=self.raise_exception
76
75
  )
77
- if self.raise_exception:
78
- raise USSOException(
79
- status_code=HTTP_401_UNAUTHORIZED,
80
- error="unauthorized",
81
- message="No token provided",
82
- )
83
- return None
84
-
85
-
86
- async def usso_exception_handler(request: Request, exc: USSOException):
87
- return JSONResponse(
88
- status_code=exc.status_code,
89
- content={"message": exc.message, "error": exc.error},
90
- )
91
-
92
-
93
- EXCEPTION_HANDLERS = {
94
- USSOException: usso_exception_handler,
95
- }
76
+ _handle_exception(
77
+ "Unauthorized",
78
+ message="No token provided",
79
+ raise_exception=self.raise_exception,
80
+ )
@@ -0,0 +1,16 @@
1
+ from fastapi import Request
2
+ from fastapi.responses import JSONResponse
3
+
4
+ from ...exceptions import USSOException
5
+
6
+
7
+ async def usso_exception_handler(request: Request, exc: USSOException):
8
+ return JSONResponse(
9
+ status_code=exc.status_code,
10
+ content={"message": exc.message, "error": exc.error},
11
+ )
12
+
13
+
14
+ EXCEPTION_HANDLERS = {
15
+ USSOException: usso_exception_handler,
16
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: usso
3
- Version: 0.28.0
3
+ Version: 0.28.1
4
4
  Summary: A plug-and-play client for integrating universal single sign-on (SSO) with Python frameworks, enabling secure and seamless authentication across microservices.
5
5
  Author-email: Mahdi Kiani <mahdikiany@gmail.com>
6
6
  Maintainer-email: Mahdi Kiani <mahdikiany@gmail.com>
@@ -19,6 +19,7 @@ src/usso/integrations/django/__init__.py
19
19
  src/usso/integrations/django/middleware.py
20
20
  src/usso/integrations/fastapi/__init__.py
21
21
  src/usso/integrations/fastapi/dependency.py
22
+ src/usso/integrations/fastapi/handler.py
22
23
  src/usso/models/user.py
23
24
  src/usso/session/__init__.py
24
25
  src/usso/session/async_session.py
@@ -0,0 +1,102 @@
1
+ import json
2
+ import os
3
+ from collections.abc import AsyncGenerator
4
+
5
+ import httpx
6
+ import pytest
7
+ import pytest_asyncio
8
+ from fastapi import Depends, WebSocket
9
+ from starlette.testclient import TestClient
10
+ from usso_jwt.algorithms import AbstractKey
11
+
12
+ from src.usso import UserData
13
+ from src.usso.exceptions import USSOException
14
+ from src.usso.integrations.fastapi import (
15
+ USSOAuthentication,
16
+ usso_exception_handler,
17
+ )
18
+
19
+
20
+ @pytest.fixture(scope="session")
21
+ def app(test_key: AbstractKey):
22
+ import fastapi
23
+
24
+ os.environ["JWT_CONFIG"] = json.dumps({
25
+ "type": "EDDSA",
26
+ "key": test_key.public_pem().decode(),
27
+ "jwt_header": {"type": "Authorization"},
28
+ })
29
+
30
+ app = fastapi.FastAPI()
31
+
32
+ app.add_exception_handler(USSOException, usso_exception_handler)
33
+
34
+ usso = USSOAuthentication()
35
+
36
+ @app.get("/user")
37
+ async def get_user(
38
+ user: UserData = Depends(usso.usso_access_security), # noqa: B008
39
+ ):
40
+ return user.model_dump()
41
+
42
+ @app.websocket("/ws")
43
+ async def websocket_endpoint(
44
+ websocket: WebSocket,
45
+ user: UserData = Depends(usso.jwt_access_security_ws),
46
+ ):
47
+ await websocket.accept()
48
+ await websocket.send_json({"msg": user.model_dump()})
49
+ # await websocket.send_json({"msg": "Hello WebSocket"})
50
+ await websocket.close()
51
+
52
+ return app
53
+
54
+
55
+ @pytest_asyncio.fixture(scope="session")
56
+ async def client(app) -> AsyncGenerator[httpx.AsyncClient]:
57
+ """Fixture to provide an AsyncClient for FastAPI app."""
58
+
59
+ async with httpx.AsyncClient(
60
+ transport=httpx.ASGITransport(app=app),
61
+ base_url="http://test.uln.me",
62
+ ) as ac:
63
+ yield ac
64
+
65
+
66
+ @pytest.mark.asyncio
67
+ async def test_get_user_no_token(client: httpx.AsyncClient):
68
+ response = await client.get("/user")
69
+ print(response.json())
70
+ assert response.status_code == 401
71
+
72
+
73
+ @pytest.mark.asyncio
74
+ async def test_get_user_with_invalid_token(client: httpx.AsyncClient):
75
+ response = await client.get(
76
+ "/user",
77
+ headers={"Authorization": "Bearer test"},
78
+ )
79
+ assert response.status_code == 401
80
+
81
+
82
+ @pytest.mark.asyncio
83
+ async def test_get_user_with_token(
84
+ client: httpx.AsyncClient,
85
+ test_valid_token: str,
86
+ test_valid_payload: dict,
87
+ ):
88
+ response = await client.get(
89
+ "/user",
90
+ headers={"Authorization": f"Bearer {test_valid_token}"},
91
+ )
92
+ assert response.status_code == 200
93
+ assert response.json().get("claims") == test_valid_payload
94
+
95
+
96
+ def test_websocket(app, test_valid_token: str, test_valid_payload: dict):
97
+ client = TestClient(app)
98
+ with client.websocket_connect(
99
+ "/ws", headers={"Authorization": f"Bearer {test_valid_token}"}
100
+ ) as websocket:
101
+ data = websocket.receive_json()
102
+ assert data.get("msg").get("claims") == test_valid_payload
@@ -1,3 +0,0 @@
1
- from .dependency import USSOAuthentication
2
-
3
- __all__ = ["USSOAuthentication"]
@@ -1,79 +0,0 @@
1
- from collections.abc import AsyncGenerator
2
-
3
- import httpx
4
- import pytest
5
- import pytest_asyncio
6
- from fastapi import Depends
7
- from usso_jwt.algorithms import AbstractKey
8
-
9
- from src.usso import AuthConfig, UserData
10
- from src.usso.exceptions import USSOException
11
- from src.usso.integrations.fastapi import USSOAuthentication
12
-
13
-
14
- @pytest.fixture(scope="session")
15
- def app(test_key: AbstractKey):
16
- import fastapi
17
-
18
- app = fastapi.FastAPI()
19
- config = AuthConfig(
20
- key=test_key.public_pem(),
21
- jwt_header={"type": "Authorization"},
22
- )
23
- usso = USSOAuthentication(jwt_config=config)
24
-
25
- async def get_current_user(request: fastapi.Request) -> UserData:
26
- return usso.usso_access_security(request)
27
-
28
- @app.get("/user")
29
- async def get_user(
30
- user: UserData = Depends(get_current_user), # noqa: B008
31
- ):
32
- print(user)
33
- return user.model_dump()
34
-
35
- return app
36
-
37
-
38
- @pytest_asyncio.fixture(scope="session")
39
- async def client(app) -> AsyncGenerator[httpx.AsyncClient]:
40
- """Fixture to provide an AsyncClient for FastAPI app."""
41
-
42
- async with httpx.AsyncClient(
43
- transport=httpx.ASGITransport(app=app),
44
- base_url="http://test.uln.me",
45
- ) as ac:
46
- yield ac
47
-
48
-
49
- @pytest.mark.asyncio
50
- async def test_get_user_no_token(client: httpx.AsyncClient):
51
- with pytest.raises(USSOException):
52
- response = await client.get("/user")
53
- print(response.json())
54
- assert response.status_code == 401
55
-
56
-
57
- @pytest.mark.asyncio
58
- async def test_get_user_with_invalid_token(client: httpx.AsyncClient):
59
- with pytest.raises(USSOException):
60
- response = await client.get(
61
- "/user",
62
- headers={"Authorization": "Bearer test"},
63
- )
64
- assert response.status_code == 401
65
-
66
-
67
- @pytest.mark.asyncio
68
- async def test_get_user_with_token(
69
- client: httpx.AsyncClient,
70
- test_valid_token: str,
71
- test_valid_payload: dict,
72
- ):
73
- response = await client.get(
74
- "/user",
75
- headers={"Authorization": f"Bearer {test_valid_token}"},
76
- )
77
- assert response.status_code == 200
78
- print(response.json(), test_valid_payload)
79
- assert response.json().get("claims") == test_valid_payload
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes