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 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
- Rules:
62
- - The final resource name must match exactly or via fnmatch.
63
- - Upper-level path parts are matched from right to left.
64
- - Wildcards are allowed in any part.
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(user_path)}")
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[-offset:]:
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 filter in filters:
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=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 = "https://sso.usso.io/api/sso/v1/apikeys/verify"
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, status_code: int, error: str, message: dict | None = None
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
- self.message = message
26
+ msg: dict = {}
22
27
  if message is None:
23
- self.message = error_messages[error]
24
- super().__init__(message)
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
- message: dict = None,
32
- detail: str = None,
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__(jwt_config=jwt_config)
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(request: Request, exc: USSOException):
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={"message": exc.message, "error": exc.error},
12
+ content={
13
+ "message": exc.message,
14
+ "error": exc.error,
15
+ "detail": exc.detail,
16
+ **exc.data,
17
+ },
11
18
  )
12
19
 
13
20
 
@@ -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
- httpx.AsyncClient.__init__(self)
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(self, method: str, url: str, **kwargs):
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)
@@ -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(self, method: str, url: str, **kwargs):
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: Any | None = None,
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.26
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.0
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,,
@@ -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