usso 0.28.26__py3-none-any.whl → 0.28.28__py3-none-any.whl
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.
- usso/api_key.py +2 -2
- usso/authorization.py +32 -62
- usso/client.py +25 -2
- usso/config.py +10 -7
- usso/exceptions.py +24 -10
- usso/integrations/django/middleware.py +1 -1
- usso/integrations/fastapi/dependency.py +7 -4
- usso/integrations/fastapi/handler.py +9 -2
- usso/session/async_session.py +7 -4
- usso/session/base_session.py +3 -3
- usso/session/session.py +7 -5
- usso/user.py +6 -6
- {usso-0.28.26.dist-info → usso-0.28.28.dist-info}/METADATA +2 -2
- usso-0.28.28.dist-info/RECORD +24 -0
- usso-0.28.26.dist-info/RECORD +0 -24
- {usso-0.28.26.dist-info → usso-0.28.28.dist-info}/WHEEL +0 -0
- {usso-0.28.26.dist-info → usso-0.28.28.dist-info}/entry_points.txt +0 -0
- {usso-0.28.26.dist-info → usso-0.28.28.dist-info}/licenses/LICENSE.txt +0 -0
- {usso-0.28.26.dist-info → usso-0.28.28.dist-info}/top_level.txt +0 -0
usso/api_key.py
CHANGED
@@ -10,7 +10,7 @@ from .user import UserData
|
|
10
10
|
logger = logging.getLogger("usso")
|
11
11
|
|
12
12
|
|
13
|
-
def _handle_exception(error_type: str, **kwargs):
|
13
|
+
def _handle_exception(error_type: str, **kwargs: dict) -> None:
|
14
14
|
"""Handle API key related exceptions."""
|
15
15
|
if kwargs.get("raise_exception", True):
|
16
16
|
raise USSOException(
|
@@ -20,7 +20,7 @@ def _handle_exception(error_type: str, **kwargs):
|
|
20
20
|
|
21
21
|
|
22
22
|
@cachetools.func.ttl_cache(maxsize=128, ttl=60)
|
23
|
-
def fetch_api_key_data(api_key_verify_url: str, api_key: str):
|
23
|
+
def fetch_api_key_data(api_key_verify_url: str, api_key: str) -> UserData:
|
24
24
|
"""Fetch user data using an API key.
|
25
25
|
|
26
26
|
Args:
|
usso/authorization.py
CHANGED
@@ -26,6 +26,9 @@ def parse_scope(scope: str) -> tuple[str, list[str], dict[str, str]]:
|
|
26
26
|
"*:*" ->
|
27
27
|
("*", ["*"], {})
|
28
28
|
|
29
|
+
"media//files" ->
|
30
|
+
("", ["media", "*", "files"], {})
|
31
|
+
|
29
32
|
Returns:
|
30
33
|
- action: str (could be empty string if no scheme present)
|
31
34
|
- path_parts: list[str]
|
@@ -47,91 +50,58 @@ def parse_scope(scope: str) -> tuple[str, list[str], dict[str, str]]:
|
|
47
50
|
query = scope[question_idx + 1 :]
|
48
51
|
filters = {k: v[0] for k, v in parse_qs(query).items()}
|
49
52
|
resource_path_parts = resource_path.split("/") if resource_path else ["*"]
|
50
|
-
return action, resource_path_parts, filters
|
51
|
-
|
53
|
+
return action, [rp or "*" for rp in resource_path_parts], filters
|
52
54
|
|
53
|
-
def is_path_match(
|
54
|
-
user_path: list[str] | str,
|
55
|
-
requested_path: list[str] | str,
|
56
|
-
strict: bool = False,
|
57
|
-
) -> bool:
|
58
|
-
"""
|
59
|
-
Match resource paths from right to left, supporting wildcards (*).
|
60
55
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
Examples = [
|
67
|
-
("files", "files", True),
|
68
|
-
("file-manager/files", "files", True),
|
69
|
-
("media/file-manager/files", "files", True),
|
70
|
-
("media//files", "files", True),
|
71
|
-
("media//files", "file-manager/files", True),
|
72
|
-
("files", "file-manager/files", True),
|
73
|
-
("*/files", "file-manager/files", True),
|
74
|
-
("*//files", "file-manager/files", True),
|
75
|
-
("//files", "file-manager/files", True),
|
76
|
-
("//files", "media/file-manager/files", True),
|
77
|
-
("media//files", "media/file-manager/files", True),
|
78
|
-
("media/files/*", "media/files/transactions", True),
|
79
|
-
("*/*/transactions", "media/files/transactions", True),
|
80
|
-
("media/*/transactions", "media/images/transactions", True),
|
81
|
-
("media//files", "media/files", True), # attention
|
82
|
-
|
83
|
-
("files", "file", False),
|
84
|
-
("files", "files/transactions", False),
|
85
|
-
("files", "media/files/transactions", False),
|
86
|
-
("media/files", "media/files/transactions", False),
|
87
|
-
("finance/*/*", "wallet", False),
|
88
|
-
]
|
89
|
-
"""
|
90
|
-
if isinstance(user_path, str):
|
91
|
-
user_parts = user_path.split("/")
|
92
|
-
elif isinstance(user_path, list):
|
93
|
-
user_parts = user_path
|
56
|
+
def _normalize_path(path: list[str] | str) -> list[str]:
|
57
|
+
if isinstance(path, str):
|
58
|
+
return path.split("/")
|
59
|
+
elif isinstance(path, list):
|
60
|
+
return path
|
94
61
|
else:
|
95
|
-
raise ValueError(f"Invalid path type: {type(
|
62
|
+
raise ValueError(f"Invalid path type: {type(path)}")
|
96
63
|
|
97
|
-
if isinstance(requested_path, str):
|
98
|
-
req_parts = requested_path.split("/")
|
99
|
-
elif isinstance(requested_path, list):
|
100
|
-
req_parts = requested_path
|
101
|
-
else:
|
102
|
-
raise ValueError(f"Invalid path type: {type(requested_path)}")
|
103
64
|
|
65
|
+
def _match_path_parts(
|
66
|
+
user_parts: list[str], req_parts: list[str], strict: bool
|
67
|
+
) -> bool:
|
104
68
|
wildcard_found = False
|
105
69
|
# Match resource name (rightmost)
|
106
70
|
if not fnmatch.fnmatch(req_parts[-1], user_parts[-1]):
|
107
71
|
return False
|
108
|
-
|
109
72
|
if "*" in user_parts[-1]:
|
110
73
|
wildcard_found = True
|
111
|
-
|
112
74
|
# Match rest of the path from right to left
|
113
75
|
user_path_parts = user_parts[:-1]
|
114
76
|
req_path_parts = req_parts[:-1]
|
115
|
-
|
116
77
|
for u, r in zip(
|
117
|
-
reversed(user_path_parts),
|
118
|
-
reversed(req_path_parts),
|
119
|
-
strict=strict,
|
78
|
+
reversed(user_path_parts), reversed(req_path_parts), strict=strict
|
120
79
|
):
|
121
80
|
if r and u and r != "*" and not fnmatch.fnmatch(r, u):
|
122
81
|
return False
|
123
82
|
if "*" in u:
|
124
83
|
wildcard_found = True
|
125
|
-
|
126
84
|
offset = len(user_path_parts) - len(req_path_parts)
|
127
85
|
if offset > 0 and wildcard_found:
|
128
|
-
for u in user_path_parts[
|
86
|
+
for u in user_path_parts[:offset]:
|
129
87
|
if u != "*":
|
130
88
|
return False
|
131
|
-
|
132
89
|
return True
|
133
90
|
|
134
91
|
|
92
|
+
def is_path_match(
|
93
|
+
user_path: list[str] | str,
|
94
|
+
requested_path: list[str] | str,
|
95
|
+
strict: bool = False,
|
96
|
+
) -> bool:
|
97
|
+
"""
|
98
|
+
Match resource paths from right to left, supporting wildcards (*).
|
99
|
+
"""
|
100
|
+
user_parts = _normalize_path(user_path)
|
101
|
+
req_parts = _normalize_path(requested_path)
|
102
|
+
return _match_path_parts(user_parts, req_parts, strict)
|
103
|
+
|
104
|
+
|
135
105
|
def is_filter_match(user_filters: dict, requested_filters: dict) -> bool:
|
136
106
|
"""All user filters must match requested filters."""
|
137
107
|
for k, v in user_filters.items():
|
@@ -222,7 +192,7 @@ def is_authorized(
|
|
222
192
|
if not is_path_match(user_path, requested_path, strict=strict):
|
223
193
|
return False
|
224
194
|
|
225
|
-
if not is_filter_match(user_filters, reuested_filter):
|
195
|
+
if not is_filter_match(user_filters, reuested_filter or {}):
|
226
196
|
return False
|
227
197
|
|
228
198
|
if requested_action:
|
@@ -256,15 +226,15 @@ def check_access(
|
|
256
226
|
if isinstance(filters, dict):
|
257
227
|
filters = [{k: v} for k, v in filters.items()]
|
258
228
|
elif filters is None:
|
259
|
-
filters = [
|
229
|
+
filters = [{}]
|
260
230
|
|
261
231
|
for scope in user_scopes:
|
262
|
-
for
|
232
|
+
for filt in filters:
|
263
233
|
if is_authorized(
|
264
234
|
user_scope=scope,
|
265
235
|
requested_path=resource_path,
|
266
236
|
requested_action=action,
|
267
|
-
reuested_filter=
|
237
|
+
reuested_filter=filt,
|
268
238
|
strict=strict,
|
269
239
|
):
|
270
240
|
return True
|
usso/client.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import logging
|
2
|
+
from urllib.parse import urlparse
|
2
3
|
|
3
4
|
import usso_jwt.exceptions
|
4
5
|
import usso_jwt.schemas
|
@@ -22,7 +23,9 @@ class UssoAuth:
|
|
22
23
|
self,
|
23
24
|
*,
|
24
25
|
jwt_config: AvailableJwtConfigs | None = None,
|
25
|
-
|
26
|
+
from_base_usso_url: str | None = None,
|
27
|
+
**kwargs: object,
|
28
|
+
) -> None:
|
26
29
|
"""Initialize the USSO authentication client.
|
27
30
|
|
28
31
|
Args:
|
@@ -31,6 +34,7 @@ class UssoAuth:
|
|
31
34
|
if jwt_config is None:
|
32
35
|
jwt_config = AuthConfig()
|
33
36
|
self.jwt_configs = AuthConfig.validate_jwt_configs(jwt_config)
|
37
|
+
self.from_base_usso_url = from_base_usso_url
|
34
38
|
|
35
39
|
def user_data_from_token(
|
36
40
|
self,
|
@@ -38,7 +42,7 @@ class UssoAuth:
|
|
38
42
|
*,
|
39
43
|
expected_token_type: str | None = "access",
|
40
44
|
raise_exception: bool = True,
|
41
|
-
**kwargs,
|
45
|
+
**kwargs: dict,
|
42
46
|
) -> UserData | None:
|
43
47
|
"""Get user data from a JWT token.
|
44
48
|
|
@@ -68,6 +72,25 @@ class UssoAuth:
|
|
68
72
|
except usso_jwt.exceptions.JWTError as e:
|
69
73
|
exp = e
|
70
74
|
|
75
|
+
if self.from_base_usso_url:
|
76
|
+
try:
|
77
|
+
jwt_obj = usso_jwt.schemas.JWT(
|
78
|
+
token=token, config=jwk_config, payload_class=UserData
|
79
|
+
)
|
80
|
+
iss = jwt_obj.unverified_payload.iss
|
81
|
+
iss_domain = urlparse(iss).netloc
|
82
|
+
jwt_obj.config.jwks_url = (
|
83
|
+
f"{self.from_base_usso_url}/.well-known/jwks.json?"
|
84
|
+
f"domain={iss_domain}"
|
85
|
+
)
|
86
|
+
if jwt_obj.verify(
|
87
|
+
expected_token_type=expected_token_type,
|
88
|
+
**kwargs,
|
89
|
+
):
|
90
|
+
return jwt_obj.payload
|
91
|
+
except usso_jwt.exceptions.JWTError as e:
|
92
|
+
exp = e
|
93
|
+
|
71
94
|
_handle_exception(
|
72
95
|
"Unauthorized",
|
73
96
|
message=str(exp) if exp else None,
|
usso/config.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import json
|
2
|
+
import os
|
2
3
|
from typing import Any, Literal, Union
|
3
4
|
|
4
5
|
import usso_jwt.config
|
@@ -7,13 +8,15 @@ from pydantic import BaseModel, model_validator
|
|
7
8
|
from .user import UserData
|
8
9
|
from .utils.string_utils import get_authorization_scheme_param
|
9
10
|
|
11
|
+
BASE_USSO_URL = os.getenv("BASE_USSO_URL") or "https://sso.usso.io"
|
12
|
+
|
10
13
|
|
11
14
|
class HeaderConfig(BaseModel):
|
12
15
|
type: Literal["Authorization", "Cookie", "CustomHeader"] = "Cookie"
|
13
16
|
name: str = "usso_access_token"
|
14
17
|
|
15
18
|
@model_validator(mode="before")
|
16
|
-
def validate_header(cls, data: dict):
|
19
|
+
def validate_header(cls, data: dict) -> dict:
|
17
20
|
if data.get("type") == "Authorization" and not data.get("name"):
|
18
21
|
data["name"] = "Bearer"
|
19
22
|
elif data.get("type") == "Cookie":
|
@@ -22,10 +25,10 @@ class HeaderConfig(BaseModel):
|
|
22
25
|
data["name"] = data.get("name", "x-usso-access-token")
|
23
26
|
return data
|
24
27
|
|
25
|
-
def __hash__(self):
|
28
|
+
def __hash__(self) -> int:
|
26
29
|
return hash(self.model_dump_json())
|
27
30
|
|
28
|
-
def get_key(self, request) -> str | None:
|
31
|
+
def get_key(self, request: object) -> str | None: # type: ignore
|
29
32
|
headers: dict[str, Any] = getattr(request, "headers", {})
|
30
33
|
cookies: dict[str, str] = getattr(
|
31
34
|
request, "cookies", headers.get("Cookie", {})
|
@@ -42,7 +45,7 @@ class HeaderConfig(BaseModel):
|
|
42
45
|
|
43
46
|
|
44
47
|
class APIHeaderConfig(HeaderConfig):
|
45
|
-
verify_endpoint: str = "
|
48
|
+
verify_endpoint: str = f"{BASE_USSO_URL}/api/sso/v1/apikeys/verify"
|
46
49
|
|
47
50
|
|
48
51
|
class AuthConfig(usso_jwt.config.JWTConfig):
|
@@ -54,18 +57,18 @@ class AuthConfig(usso_jwt.config.JWTConfig):
|
|
54
57
|
jwt_header: HeaderConfig | None = HeaderConfig()
|
55
58
|
static_api_keys: list[str] | None = None
|
56
59
|
|
57
|
-
def get_api_key(self, request) -> str | None:
|
60
|
+
def get_api_key(self, request: object) -> str | None:
|
58
61
|
if self.api_key_header:
|
59
62
|
return self.api_key_header.get_key(request)
|
60
63
|
return None
|
61
64
|
|
62
|
-
def get_jwt(self, request) -> str | None:
|
65
|
+
def get_jwt(self, request: object) -> str | None:
|
63
66
|
if self.jwt_header:
|
64
67
|
return self.jwt_header.get_key(request)
|
65
68
|
return None
|
66
69
|
|
67
70
|
def verify_token(
|
68
|
-
self, token: str, *, raise_exception: bool = True, **kwargs
|
71
|
+
self, token: str, *, raise_exception: bool = True, **kwargs: dict
|
69
72
|
) -> bool:
|
70
73
|
from usso_jwt import exceptions as jwt_exceptions
|
71
74
|
from usso_jwt import schemas
|
usso/exceptions.py
CHANGED
@@ -14,30 +14,44 @@ error_messages = {
|
|
14
14
|
|
15
15
|
class USSOException(Exception):
|
16
16
|
def __init__(
|
17
|
-
self,
|
18
|
-
|
17
|
+
self,
|
18
|
+
status_code: int,
|
19
|
+
error: str,
|
20
|
+
detail: str | None = None,
|
21
|
+
message: dict | None = None,
|
22
|
+
**kwargs: dict,
|
23
|
+
) -> None:
|
19
24
|
self.status_code = status_code
|
20
25
|
self.error = error
|
21
|
-
|
26
|
+
msg: dict = {}
|
22
27
|
if message is None:
|
23
|
-
|
24
|
-
|
28
|
+
if detail:
|
29
|
+
msg["en"] = detail
|
30
|
+
else:
|
31
|
+
msg["en"] = error_messages.get(error, error)
|
32
|
+
else:
|
33
|
+
msg = message
|
34
|
+
|
35
|
+
self.message = msg
|
36
|
+
self.detail = detail or str(self.message)
|
37
|
+
self.data = kwargs
|
38
|
+
super().__init__(detail)
|
25
39
|
|
26
40
|
|
27
41
|
class PermissionDenied(USSOException):
|
28
42
|
def __init__(
|
29
43
|
self,
|
30
44
|
error: str = "permission_denied",
|
31
|
-
|
32
|
-
|
33
|
-
**kwargs,
|
34
|
-
):
|
45
|
+
detail: str | None = None,
|
46
|
+
message: dict | None = None,
|
47
|
+
**kwargs: dict,
|
48
|
+
) -> None:
|
35
49
|
super().__init__(
|
36
50
|
403, error=error, message=message, detail=detail, **kwargs
|
37
51
|
)
|
38
52
|
|
39
53
|
|
40
|
-
def _handle_exception(error_type: str, **kwargs):
|
54
|
+
def _handle_exception(error_type: str, **kwargs: dict) -> None:
|
41
55
|
"""Handle JWT-related exceptions."""
|
42
56
|
if kwargs.get("raise_exception", True):
|
43
57
|
raise USSOException(
|
@@ -17,7 +17,7 @@ class USSOAuthenticationMiddleware(MiddlewareMixin):
|
|
17
17
|
def jwt_config(self) -> AuthConfig:
|
18
18
|
return settings.USSO_JWT_CONFIG
|
19
19
|
|
20
|
-
def process_request(self, request: HttpRequest):
|
20
|
+
def process_request(self, request: HttpRequest) -> None:
|
21
21
|
"""
|
22
22
|
Middleware to authenticate users by JWT token and create or
|
23
23
|
return a user in the database.
|
@@ -14,15 +14,20 @@ class USSOAuthentication(UssoAuth):
|
|
14
14
|
def __init__(
|
15
15
|
self,
|
16
16
|
jwt_config: AvailableJwtConfigs | None = None,
|
17
|
+
*,
|
17
18
|
raise_exception: bool = True,
|
18
19
|
expected_token_type: str = "access",
|
19
|
-
|
20
|
+
from_base_usso_url: str | None = None,
|
21
|
+
) -> None:
|
20
22
|
if jwt_config is None:
|
21
23
|
jwt_config = AuthConfig()
|
22
24
|
|
23
|
-
super().__init__(
|
25
|
+
super().__init__(
|
26
|
+
jwt_config=jwt_config, from_base_usso_url=from_base_usso_url
|
27
|
+
)
|
24
28
|
self.raise_exception = raise_exception
|
25
29
|
self.expected_token_type = expected_token_type
|
30
|
+
self.from_base_usso_url = from_base_usso_url
|
26
31
|
|
27
32
|
def __call__(self, request: Request) -> UserData:
|
28
33
|
return self.usso_access_security(request)
|
@@ -41,7 +46,6 @@ class USSOAuthentication(UssoAuth):
|
|
41
46
|
return token
|
42
47
|
return None
|
43
48
|
|
44
|
-
# @instance_method
|
45
49
|
def usso_access_security(self, request: Request) -> UserData | None:
|
46
50
|
"""Return the user associated with a token value."""
|
47
51
|
api_key = self.get_request_api_key(request)
|
@@ -62,7 +66,6 @@ class USSOAuthentication(UssoAuth):
|
|
62
66
|
raise_exception=self.raise_exception,
|
63
67
|
)
|
64
68
|
|
65
|
-
# @instance_method
|
66
69
|
def jwt_access_security_ws(self, websocket: WebSocket) -> UserData | None:
|
67
70
|
"""Return the user associated with a token value."""
|
68
71
|
api_key = self.get_request_api_key(websocket)
|
@@ -4,10 +4,17 @@ from fastapi.responses import JSONResponse
|
|
4
4
|
from ...exceptions import USSOException
|
5
5
|
|
6
6
|
|
7
|
-
async def usso_exception_handler(
|
7
|
+
async def usso_exception_handler(
|
8
|
+
request: Request, exc: USSOException
|
9
|
+
) -> JSONResponse:
|
8
10
|
return JSONResponse(
|
9
11
|
status_code=exc.status_code,
|
10
|
-
content={
|
12
|
+
content={
|
13
|
+
"message": exc.message,
|
14
|
+
"error": exc.error,
|
15
|
+
"detail": exc.detail,
|
16
|
+
**exc.data,
|
17
|
+
},
|
11
18
|
)
|
12
19
|
|
13
20
|
|
usso/session/async_session.py
CHANGED
@@ -17,8 +17,9 @@ class AsyncUssoSession(httpx.AsyncClient, BaseUssoSession):
|
|
17
17
|
usso_api_key: str | None = os.getenv("USSO_ADMIN_API_KEY"),
|
18
18
|
user_id: str | None = None,
|
19
19
|
client: "AsyncUssoSession" = None,
|
20
|
-
|
21
|
-
|
20
|
+
**kwargs: dict,
|
21
|
+
) -> None:
|
22
|
+
httpx.AsyncClient.__init__(self, **kwargs)
|
22
23
|
BaseUssoSession.__init__(
|
23
24
|
self,
|
24
25
|
usso_base_url=usso_base_url,
|
@@ -100,7 +101,7 @@ class AsyncUssoSession(httpx.AsyncClient, BaseUssoSession):
|
|
100
101
|
)
|
101
102
|
return self._handle_refresh_response(response)
|
102
103
|
|
103
|
-
async def get_session(self):
|
104
|
+
async def get_session(self) -> "AsyncUssoSession":
|
104
105
|
if hasattr(self, "api_key") and self.api_key:
|
105
106
|
return self
|
106
107
|
|
@@ -108,6 +109,8 @@ class AsyncUssoSession(httpx.AsyncClient, BaseUssoSession):
|
|
108
109
|
await self._refresh()
|
109
110
|
return self
|
110
111
|
|
111
|
-
async def _request(
|
112
|
+
async def _request(
|
113
|
+
self, method: str, url: str, **kwargs: dict
|
114
|
+
) -> httpx.Response:
|
112
115
|
session = await self.get_session()
|
113
116
|
return await session.request(method, url, **kwargs)
|
usso/session/base_session.py
CHANGED
@@ -14,7 +14,7 @@ class BaseUssoSession:
|
|
14
14
|
app_secret: str | None = None,
|
15
15
|
usso_url: str = "https://sso.usso.io",
|
16
16
|
client: Optional["BaseUssoSession"] = None,
|
17
|
-
):
|
17
|
+
) -> None:
|
18
18
|
if client:
|
19
19
|
self.copy_attributes_from(client)
|
20
20
|
return
|
@@ -54,7 +54,7 @@ class BaseUssoSession:
|
|
54
54
|
self.access_token = None
|
55
55
|
self.headers = getattr(self, "headers", {})
|
56
56
|
|
57
|
-
def copy_attributes_from(self, client: "BaseUssoSession"):
|
57
|
+
def copy_attributes_from(self, client: "BaseUssoSession") -> None:
|
58
58
|
self.usso_url = client.usso_url
|
59
59
|
self.usso_refresh_url = client.usso_refresh_url
|
60
60
|
self._refresh_token = client._refresh_token
|
@@ -64,7 +64,7 @@ class BaseUssoSession:
|
|
64
64
|
self.headers = client.headers.copy()
|
65
65
|
|
66
66
|
@property
|
67
|
-
def refresh_token(self):
|
67
|
+
def refresh_token(self) -> JWT:
|
68
68
|
if (
|
69
69
|
self._refresh_token
|
70
70
|
and self._refresh_token.verify( # noqa: W503
|
usso/session/session.py
CHANGED
@@ -14,8 +14,8 @@ class UssoSession(httpx.Client, BaseUssoSession):
|
|
14
14
|
refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
|
15
15
|
usso_url: str | None = os.getenv("USSO_URL"),
|
16
16
|
client: "UssoSession" = None,
|
17
|
-
**kwargs,
|
18
|
-
):
|
17
|
+
**kwargs: dict,
|
18
|
+
) -> None:
|
19
19
|
httpx.Client.__init__(self, **kwargs)
|
20
20
|
|
21
21
|
BaseUssoSession.__init__(
|
@@ -28,7 +28,7 @@ class UssoSession(httpx.Client, BaseUssoSession):
|
|
28
28
|
if not self.api_key:
|
29
29
|
self._refresh()
|
30
30
|
|
31
|
-
def _refresh(self):
|
31
|
+
def _refresh(self) -> dict:
|
32
32
|
assert self.refresh_token, "refresh_token is required"
|
33
33
|
|
34
34
|
response = httpx.post(
|
@@ -43,7 +43,7 @@ class UssoSession(httpx.Client, BaseUssoSession):
|
|
43
43
|
self.headers.update({"Authorization": f"Bearer {self.access_token}"})
|
44
44
|
return response.json()
|
45
45
|
|
46
|
-
def get_session(self):
|
46
|
+
def get_session(self) -> "UssoSession":
|
47
47
|
if self.api_key:
|
48
48
|
return self
|
49
49
|
|
@@ -51,6 +51,8 @@ class UssoSession(httpx.Client, BaseUssoSession):
|
|
51
51
|
self._refresh()
|
52
52
|
return self
|
53
53
|
|
54
|
-
def _request(
|
54
|
+
def _request(
|
55
|
+
self, method: str, url: str, **kwargs: dict
|
56
|
+
) -> httpx.Response:
|
55
57
|
self.get_session()
|
56
58
|
return super().request(self, method, url, **kwargs)
|
usso/user.py
CHANGED
@@ -55,8 +55,8 @@ class UserData(BaseModel):
|
|
55
55
|
acr: str | None = None,
|
56
56
|
amr: list[str] | None = None,
|
57
57
|
signing_level: str | None = None,
|
58
|
-
**kwargs,
|
59
|
-
):
|
58
|
+
**kwargs: dict,
|
59
|
+
) -> None:
|
60
60
|
super().__init__(
|
61
61
|
jti=jti,
|
62
62
|
token_type=token_type,
|
@@ -109,9 +109,9 @@ class UserData(BaseModel):
|
|
109
109
|
self,
|
110
110
|
*,
|
111
111
|
mode: Literal["json", "python"] | str = "python",
|
112
|
-
include=None,
|
113
|
-
exclude=None,
|
114
|
-
context:
|
112
|
+
include: set[str] | list[str] | None = None,
|
113
|
+
exclude: set[str] | list[str] | None = None,
|
114
|
+
context: object | None = None,
|
115
115
|
by_alias: bool | None = None,
|
116
116
|
exclude_unset: bool = False,
|
117
117
|
exclude_defaults: bool = False,
|
@@ -120,7 +120,7 @@ class UserData(BaseModel):
|
|
120
120
|
warnings: bool | Literal["none", "warn", "error"] = True,
|
121
121
|
fallback: Callable[[Any], Any] | None = None,
|
122
122
|
serialize_as_any: bool = False,
|
123
|
-
):
|
123
|
+
) -> dict:
|
124
124
|
return super().model_dump(
|
125
125
|
mode=mode,
|
126
126
|
include=include,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: usso
|
3
|
-
Version: 0.28.
|
3
|
+
Version: 0.28.28
|
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>
|
@@ -28,7 +28,7 @@ Requires-Dist: cachetools
|
|
28
28
|
Requires-Dist: singleton_package
|
29
29
|
Requires-Dist: json-advanced
|
30
30
|
Requires-Dist: httpx
|
31
|
-
Requires-Dist: usso-jwt>=0.2.
|
31
|
+
Requires-Dist: usso-jwt>=0.2.6
|
32
32
|
Provides-Extra: fastapi
|
33
33
|
Requires-Dist: fastapi>=0.65.0; extra == "fastapi"
|
34
34
|
Requires-Dist: uvicorn[standard]>=0.13.0; extra == "fastapi"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
usso/__init__.py,sha256=ot4Q5ouLGe505DGFAxQP4p4yZLLaBLqbHmCF1OvHG1M,585
|
2
|
+
usso/api_key.py,sha256=LtBY86HE27xTk-GCixTL2gyikuIV4XBYWY4OknjUgTk,1262
|
3
|
+
usso/authorization.py,sha256=5cROjDZkmUs7eYav32h20WxOexucaF2d4i3_MuKhQ9A,8264
|
4
|
+
usso/client.py,sha256=GQXX9Ifxfrr7sSNMdj4IDWb8-JmkWshb0-TMhUmYCA8,3551
|
5
|
+
usso/config.py,sha256=okT-Z6M3u-URRsBAftpxF0ZO4tAfDZGH5pA6qCip0GM,3878
|
6
|
+
usso/exceptions.py,sha256=hDxw475zvF55FTEqkfhZfciVXXGiB8pyWgqF2HiUiio,1743
|
7
|
+
usso/user.py,sha256=uWL5fkD1QSV6H5qC690iS9MJWX5AvDze6c24l3sXvB0,3846
|
8
|
+
usso/integrations/django/__init__.py,sha256=dKpbffHS5ouGtW6ooI2ivzjPmH_1rOBny85htR-KqrY,97
|
9
|
+
usso/integrations/django/middleware.py,sha256=LEPb2LkGET47cgGpydcylfBabndX4Hycyzj-xdfREug,3453
|
10
|
+
usso/integrations/fastapi/__init__.py,sha256=ohToiqutHu3Okr8naunssDkamj1OdiG4OpPdBW0rt7U,204
|
11
|
+
usso/integrations/fastapi/dependency.py,sha256=3ughk-H8yjxkH0fLjdBv-iw0hA3E84XfNs9dCv_2M-o,2833
|
12
|
+
usso/integrations/fastapi/handler.py,sha256=FcYRWcYsiKNygjAWS1elcy_QQ6neCNsUEE8WyMDtMgA,501
|
13
|
+
usso/session/__init__.py,sha256=tE4qWUdSI7iN_pywm47Mg8NKOTBa2nCNwCy3wCZWRmU,124
|
14
|
+
usso/session/async_session.py,sha256=iu-bnZHe9_ODSXax_WsclJxtwZ9ClVt7KBl3RysYE-U,4073
|
15
|
+
usso/session/base_session.py,sha256=M35m_jBkdGHVPL4R3djuJDdE1aONZnoRvMv-FVrhyaU,2582
|
16
|
+
usso/session/session.py,sha256=6f0zz1F_p4hbZgvCAdcMur__U_RaTsZ5R2658e1W-t8,1714
|
17
|
+
usso/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
18
|
+
usso/utils/string_utils.py,sha256=7tziAa2Cwa7xhwM_NF4DSY3BHoqVaWgJ21VuV8LvhrY,253
|
19
|
+
usso-0.28.28.dist-info/licenses/LICENSE.txt,sha256=ceC9ZJOV9H6CtQDcYmHOS46NA3dHJ_WD4J9blH513pc,1081
|
20
|
+
usso-0.28.28.dist-info/METADATA,sha256=Fh4LONujiTalgXOCgqHjvfv_3pLoeL-oQOJNB9Ucry4,5061
|
21
|
+
usso-0.28.28.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
22
|
+
usso-0.28.28.dist-info/entry_points.txt,sha256=4Zgpm5ELaAWPf0jPGJFz1_X69H7un8ycT3WdGoJ0Vvk,35
|
23
|
+
usso-0.28.28.dist-info/top_level.txt,sha256=g9Jf6h1Oyidh0vPiFni7UHInTJjSvu6cUalpLTIvthg,5
|
24
|
+
usso-0.28.28.dist-info/RECORD,,
|
usso-0.28.26.dist-info/RECORD
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
usso/__init__.py,sha256=ot4Q5ouLGe505DGFAxQP4p4yZLLaBLqbHmCF1OvHG1M,585
|
2
|
-
usso/api_key.py,sha256=tL5aqrmNHs9GKhCQ5NdbSchvR0qCjdZYZdkHwNONwno,1236
|
3
|
-
usso/authorization.py,sha256=c2zAR6UBkDtFQBvxUb7OJxV72WF6win4Nx8kDwYec58,9400
|
4
|
-
usso/client.py,sha256=9YTyvro3oz24Pr3i1Dit2R2dpIPsGsuIOol66-8VEyI,2636
|
5
|
-
usso/config.py,sha256=7GDAh-yHGYppfkhvrwJykhwJp4HH8P_qPAoGcK8_PZQ,3741
|
6
|
-
usso/exceptions.py,sha256=ggYczQ2eGUH9nBxRYVmOk-6IRSwY8NgEKjMPcE0E5YM,1385
|
7
|
-
usso/user.py,sha256=YD109KyK0W7LWIH-bXYgtJ53b7Ipb9tLLhwXvwQWyrs,3759
|
8
|
-
usso/integrations/django/__init__.py,sha256=dKpbffHS5ouGtW6ooI2ivzjPmH_1rOBny85htR-KqrY,97
|
9
|
-
usso/integrations/django/middleware.py,sha256=AZKYZ4UPNmyxcD3ANgp0y_fdrFvVQdHBqyYxo5XhQUs,3445
|
10
|
-
usso/integrations/fastapi/__init__.py,sha256=ohToiqutHu3Okr8naunssDkamj1OdiG4OpPdBW0rt7U,204
|
11
|
-
usso/integrations/fastapi/dependency.py,sha256=Ik1x1tP1QiZ2czr6CYD0gS9Q3P4eUD35gQEANYuESII,2699
|
12
|
-
usso/integrations/fastapi/handler.py,sha256=MNDoBYdySumFsBgVw-xir3jXXH63KehFXKCh-pNnNZQ,386
|
13
|
-
usso/session/__init__.py,sha256=tE4qWUdSI7iN_pywm47Mg8NKOTBa2nCNwCy3wCZWRmU,124
|
14
|
-
usso/session/async_session.py,sha256=eQQh2DXiaHdballRjePa8GSI9GmGsxNDU7vTfwh8mRQ,3971
|
15
|
-
usso/session/base_session.py,sha256=O3tEltMhlwkEz1GGbjE4iXPwSlLaUW2juUt9RDSLrHI,2559
|
16
|
-
usso/session/session.py,sha256=briCgDMoF-b59H6Aie_Lmjy4qnPBBSmKnVhAwef34F0,1637
|
17
|
-
usso/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
18
|
-
usso/utils/string_utils.py,sha256=7tziAa2Cwa7xhwM_NF4DSY3BHoqVaWgJ21VuV8LvhrY,253
|
19
|
-
usso-0.28.26.dist-info/licenses/LICENSE.txt,sha256=ceC9ZJOV9H6CtQDcYmHOS46NA3dHJ_WD4J9blH513pc,1081
|
20
|
-
usso-0.28.26.dist-info/METADATA,sha256=K-9Uda3H6dzs1rieU6Idy3tcGJnYVtJsi1zyTDRnT2M,5061
|
21
|
-
usso-0.28.26.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
22
|
-
usso-0.28.26.dist-info/entry_points.txt,sha256=4Zgpm5ELaAWPf0jPGJFz1_X69H7un8ycT3WdGoJ0Vvk,35
|
23
|
-
usso-0.28.26.dist-info/top_level.txt,sha256=g9Jf6h1Oyidh0vPiFni7UHInTJjSvu6cUalpLTIvthg,5
|
24
|
-
usso-0.28.26.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|