fastlifeweb 0.22.0__py3-none-any.whl → 0.23.0__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.
- CHANGELOG.md +14 -0
- fastlife/__init__.py +29 -3
- fastlife/adapters/fastapi/request.py +4 -4
- fastlife/adapters/fastapi/routing/route.py +1 -1
- fastlife/config/configurator.py +3 -3
- fastlife/domain/model/request.py +5 -2
- fastlife/domain/model/security_policy.py +78 -5
- fastlife/service/check_permission.py +2 -0
- fastlife/service/locale_negociator.py +4 -2
- fastlife/service/security_policy.py +67 -20
- {fastlifeweb-0.22.0.dist-info → fastlifeweb-0.23.0.dist-info}/METADATA +1 -1
- {fastlifeweb-0.22.0.dist-info → fastlifeweb-0.23.0.dist-info}/RECORD +15 -15
- {fastlifeweb-0.22.0.dist-info → fastlifeweb-0.23.0.dist-info}/WHEEL +0 -0
- {fastlifeweb-0.22.0.dist-info → fastlifeweb-0.23.0.dist-info}/entry_points.txt +0 -0
- {fastlifeweb-0.22.0.dist-info → fastlifeweb-0.23.0.dist-info}/licenses/LICENSE +0 -0
CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## 0.23.0 - Released on 2024-12-04
|
2
|
+
* Update Request type.
|
3
|
+
* Breaking changes: Request[TUser, TRegistry] -> Request[TRegistry, TIdentity, TClaimedIdentity].
|
4
|
+
* Update SecurityPolicy, designed for MFA by default.
|
5
|
+
* Breaking changes: new abstract method added. build_authentication_state.
|
6
|
+
* Breaking changes: there is no more get_authenticated_userid method.
|
7
|
+
* The identity method is not abstract anymore, result comes from the build_authentication_state.
|
8
|
+
* New method get_authentication_state, claimed_identity and pre_remember.
|
9
|
+
* Add a AbstractNoMFASecurityPolicy that build a AbstractSecurityPolicy without TClaimedIdentity as None.
|
10
|
+
* New ACL type added to raise 401 errors due to missing MFA which may not be same url as tu login/password.
|
11
|
+
|
12
|
+
## 0.22.1 - Released on 2024-11-27
|
13
|
+
* Improve Request typing
|
14
|
+
|
1
15
|
## 0.22.0 - Released on 2024-11-23
|
2
16
|
* Add a way to add fatal errors on form in order to display an error block.
|
3
17
|
* The localizer can be called gettext in the depency in order to simple translation.
|
fastlife/__init__.py
CHANGED
@@ -7,7 +7,13 @@ from fastapi.responses import RedirectResponse
|
|
7
7
|
|
8
8
|
from .adapters.fastapi.form import form_model
|
9
9
|
from .adapters.fastapi.localizer import Localizer
|
10
|
-
from .adapters.fastapi.request import
|
10
|
+
from .adapters.fastapi.request import (
|
11
|
+
AnyRequest,
|
12
|
+
Registry,
|
13
|
+
Request,
|
14
|
+
get_registry,
|
15
|
+
get_request,
|
16
|
+
)
|
11
17
|
from .config import (
|
12
18
|
Configurator,
|
13
19
|
GenericConfigurator,
|
@@ -21,17 +27,27 @@ from .domain.model.form import FormModel
|
|
21
27
|
from .domain.model.request import GenericRequest
|
22
28
|
from .domain.model.security_policy import (
|
23
29
|
Allowed,
|
30
|
+
Anonymous,
|
31
|
+
Authenticated,
|
32
|
+
AuthenticationState,
|
24
33
|
Denied,
|
25
34
|
Forbidden,
|
26
35
|
HasPermission,
|
36
|
+
NoMFAAuthenticationState,
|
37
|
+
PendingMFA,
|
38
|
+
PreAuthenticated,
|
27
39
|
Unauthenticated,
|
28
40
|
Unauthorized,
|
29
41
|
)
|
30
42
|
from .domain.model.template import JinjaXTemplate
|
31
43
|
|
32
44
|
# from .request.form_data import model
|
33
|
-
from .service.registry import DefaultRegistry, GenericRegistry
|
34
|
-
from .service.security_policy import
|
45
|
+
from .service.registry import DefaultRegistry, GenericRegistry, TRegistry, TSettings
|
46
|
+
from .service.security_policy import (
|
47
|
+
AbstractNoMFASecurityPolicy,
|
48
|
+
AbstractSecurityPolicy,
|
49
|
+
InsecurePolicy,
|
50
|
+
)
|
35
51
|
from .settings import Settings
|
36
52
|
|
37
53
|
__all__ = [
|
@@ -47,6 +63,9 @@ __all__ = [
|
|
47
63
|
"resource_view",
|
48
64
|
"Configurator",
|
49
65
|
"DefaultRegistry",
|
66
|
+
"TSettings",
|
67
|
+
"TRegistry",
|
68
|
+
"get_registry",
|
50
69
|
# Form
|
51
70
|
"FormModel",
|
52
71
|
"form_model",
|
@@ -60,13 +79,20 @@ __all__ = [
|
|
60
79
|
"RedirectResponse",
|
61
80
|
# Security
|
62
81
|
"AbstractSecurityPolicy",
|
82
|
+
"AbstractNoMFASecurityPolicy",
|
63
83
|
"HasPermission",
|
64
84
|
"Unauthenticated",
|
85
|
+
"PreAuthenticated",
|
65
86
|
"Allowed",
|
66
87
|
"Denied",
|
67
88
|
"Unauthorized",
|
68
89
|
"Forbidden",
|
69
90
|
"InsecurePolicy",
|
91
|
+
"Anonymous",
|
92
|
+
"PendingMFA",
|
93
|
+
"Authenticated",
|
94
|
+
"AuthenticationState",
|
95
|
+
"NoMFAAuthenticationState",
|
70
96
|
# Template
|
71
97
|
"JinjaXTemplate",
|
72
98
|
# i18n
|
@@ -9,18 +9,18 @@ from fastlife.domain.model.request import GenericRequest
|
|
9
9
|
from fastlife.service.registry import DefaultRegistry
|
10
10
|
|
11
11
|
|
12
|
-
def get_request(request: FastAPIRequest) -> GenericRequest[Any]:
|
12
|
+
def get_request(request: FastAPIRequest) -> GenericRequest[Any, Any, Any]:
|
13
13
|
"""Return the Fastlife Request object."""
|
14
14
|
return request # type: ignore
|
15
15
|
|
16
16
|
|
17
|
-
Request = Annotated[GenericRequest[DefaultRegistry], Depends(get_request)]
|
17
|
+
Request = Annotated[GenericRequest[DefaultRegistry, Any, Any], Depends(get_request)]
|
18
18
|
"""A request that is associated to the default registry."""
|
19
19
|
# FastAPI handle its Request objects using a lenient_issubclass,
|
20
|
-
# basically a issubclass(Request),
|
20
|
+
# basically a issubclass(Request), does not work with Generic[T].
|
21
21
|
|
22
22
|
|
23
|
-
AnyRequest = Annotated[GenericRequest[Any], Depends(get_request)]
|
23
|
+
AnyRequest = Annotated[GenericRequest[Any, Any, Any], Depends(get_request)]
|
24
24
|
"""A request version that is associated to the any registry."""
|
25
25
|
|
26
26
|
|
@@ -41,7 +41,7 @@ class Route(APIRoute):
|
|
41
41
|
orig_route_handler = super().get_route_handler()
|
42
42
|
|
43
43
|
async def route_handler(request: StarletteRequest) -> Response:
|
44
|
-
req = GenericRequest(self._registry, request)
|
44
|
+
req = GenericRequest[Any, Any, Any](self._registry, request)
|
45
45
|
return await orig_route_handler(req)
|
46
46
|
|
47
47
|
return route_handler
|
fastlife/config/configurator.py
CHANGED
@@ -144,7 +144,7 @@ class GenericConfigurator(Generic[TRegistry]):
|
|
144
144
|
self._route_prefix: str = ""
|
145
145
|
self._routers: dict[str, Router] = defaultdict(Router)
|
146
146
|
self._security_policies: dict[
|
147
|
-
str, type[AbstractSecurityPolicy[Any, TRegistry]]
|
147
|
+
str, type[AbstractSecurityPolicy[Any, Any, TRegistry]]
|
148
148
|
] = {}
|
149
149
|
|
150
150
|
self._registered_permissions: set[str] = set()
|
@@ -322,7 +322,7 @@ class GenericConfigurator(Generic[TRegistry]):
|
|
322
322
|
return self
|
323
323
|
|
324
324
|
def set_security_policy(
|
325
|
-
self, security_policy: "type[AbstractSecurityPolicy[Any,
|
325
|
+
self, security_policy: "type[AbstractSecurityPolicy[TRegistry, Any, Any]]"
|
326
326
|
) -> Self:
|
327
327
|
"""
|
328
328
|
Set a security policy for the application.
|
@@ -594,7 +594,7 @@ class GenericConfigurator(Generic[TRegistry]):
|
|
594
594
|
# class is wrong.
|
595
595
|
# Until we store a security policy per rooter, we rebuild an
|
596
596
|
# incomplete request here.
|
597
|
-
req = GenericRequest[DefaultRegistry](self.registry, request)
|
597
|
+
req = GenericRequest[DefaultRegistry, Any, Any](self.registry, request)
|
598
598
|
resp = handler(req, exc)
|
599
599
|
if isinstance(resp, Response):
|
600
600
|
return resp
|
fastlife/domain/model/request.py
CHANGED
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Any, Generic
|
|
5
5
|
from starlette.requests import Request as BaseRequest
|
6
6
|
|
7
7
|
from fastlife.domain.model.csrf import CSRFToken, create_csrf_token
|
8
|
+
from fastlife.domain.model.security_policy import TClaimedIdentity, TIdentity
|
8
9
|
from fastlife.service.registry import TRegistry
|
9
10
|
|
10
11
|
if TYPE_CHECKING:
|
@@ -14,7 +15,7 @@ if TYPE_CHECKING:
|
|
14
15
|
)
|
15
16
|
|
16
17
|
|
17
|
-
class GenericRequest(BaseRequest, Generic[TRegistry]):
|
18
|
+
class GenericRequest(BaseRequest, Generic[TRegistry, TIdentity, TClaimedIdentity]):
|
18
19
|
"""HTTP Request representation."""
|
19
20
|
|
20
21
|
registry: TRegistry
|
@@ -22,7 +23,9 @@ class GenericRequest(BaseRequest, Generic[TRegistry]):
|
|
22
23
|
locale_name: str
|
23
24
|
"""Request locale used for the i18n of the response."""
|
24
25
|
|
25
|
-
security_policy:
|
26
|
+
security_policy: (
|
27
|
+
"AbstractSecurityPolicy[TRegistry, TIdentity, TClaimedIdentity] | None"
|
28
|
+
)
|
26
29
|
"""Request locale used for the i18n of the response."""
|
27
30
|
|
28
31
|
renderer_globals: dict[str, Any]
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
import logging
|
4
4
|
from collections.abc import Callable, Coroutine
|
5
|
-
from typing import Any, Literal, TypeVar
|
5
|
+
from typing import Any, Generic, Literal, TypeVar
|
6
6
|
|
7
7
|
from fastapi import HTTPException
|
8
8
|
from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
|
@@ -10,13 +10,62 @@ from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
|
|
10
10
|
CheckPermissionHook = Callable[..., Coroutine[Any, Any, None]] | Callable[..., None]
|
11
11
|
CheckPermission = Callable[[str], CheckPermissionHook]
|
12
12
|
|
13
|
-
|
13
|
+
TClaimedIdentity = TypeVar("TClaimedIdentity")
|
14
|
+
TIdentity = TypeVar("TIdentity")
|
14
15
|
|
15
16
|
log = logging.getLogger(__name__)
|
16
17
|
|
17
18
|
|
19
|
+
class _Anonymous: ...
|
20
|
+
|
21
|
+
|
22
|
+
Anonymous = _Anonymous()
|
23
|
+
"""
|
24
|
+
The user is not authenticated.
|
25
|
+
"""
|
26
|
+
|
27
|
+
|
28
|
+
class PendingMFA(Generic[TClaimedIdentity]):
|
29
|
+
"""
|
30
|
+
The user provided its identity, usually validated with a first factor,
|
31
|
+
such as a password but it has not totally proved its authentication
|
32
|
+
by a second or many other factors of authentication.
|
33
|
+
The type TClaimedIdentity will store the relevant informations during
|
34
|
+
this authentication phase.
|
35
|
+
"""
|
36
|
+
|
37
|
+
claimed: TClaimedIdentity | None
|
38
|
+
__match_args__ = ("claimed",)
|
39
|
+
|
40
|
+
def __init__(self, claimed: TClaimedIdentity) -> None:
|
41
|
+
self.claimed = claimed
|
42
|
+
|
43
|
+
|
44
|
+
class Authenticated(Generic[TIdentity]):
|
45
|
+
"""The identity has been validated."""
|
46
|
+
|
47
|
+
__match_args__ = ("identity",)
|
48
|
+
|
49
|
+
def __init__(self, identity: TIdentity) -> None:
|
50
|
+
self.identity = identity
|
51
|
+
|
52
|
+
|
53
|
+
AuthenticationState = (
|
54
|
+
_Anonymous | PendingMFA[TClaimedIdentity] | Authenticated[TIdentity]
|
55
|
+
)
|
56
|
+
"""
|
57
|
+
Type representing the state of an authentication.
|
58
|
+
"""
|
59
|
+
|
60
|
+
NoMFAAuthenticationState = AuthenticationState[None, TIdentity]
|
61
|
+
"""
|
62
|
+
Type representing a state of authentication when no multiple factor of authentication
|
63
|
+
is involved.
|
64
|
+
"""
|
65
|
+
|
66
|
+
|
18
67
|
class Unauthorized(HTTPException):
|
19
|
-
"""An exception raised to stop a request
|
68
|
+
"""An exception raised to stop a request execution and return a 401 HTTP Error."""
|
20
69
|
|
21
70
|
def __init__(
|
22
71
|
self,
|
@@ -27,8 +76,22 @@ class Unauthorized(HTTPException):
|
|
27
76
|
super().__init__(status_code, detail, headers)
|
28
77
|
|
29
78
|
|
79
|
+
class MFARequired(Unauthorized):
|
80
|
+
"""
|
81
|
+
An exception raised to stop a request execution and return a 401 HTTP Error for MFA.
|
82
|
+
"""
|
83
|
+
|
84
|
+
def __init__(
|
85
|
+
self,
|
86
|
+
status_code: int = HTTP_401_UNAUTHORIZED,
|
87
|
+
detail: str = "MFA Required",
|
88
|
+
headers: dict[str, str] | None = None,
|
89
|
+
) -> None:
|
90
|
+
super().__init__(status_code, detail, headers)
|
91
|
+
|
92
|
+
|
30
93
|
class Forbidden(HTTPException):
|
31
|
-
"""An exception raised to stop a request
|
94
|
+
"""An exception raised to stop a request execution and return a 403 HTTP Error."""
|
32
95
|
|
33
96
|
def __init__(
|
34
97
|
self,
|
@@ -59,7 +122,7 @@ class HasPermission(int, metaclass=BoolMeta):
|
|
59
122
|
or the frontend may use the information to adapt its interface.
|
60
123
|
"""
|
61
124
|
|
62
|
-
kind: Literal["allowed", "unauthenticated", "denied"]
|
125
|
+
kind: Literal["allowed", "unauthenticated", "mfa_required", "denied"]
|
63
126
|
"""
|
64
127
|
Identified basic information of the response.
|
65
128
|
It distinguished unauthenticated and denied to eventually raised 401 over 403 error.
|
@@ -96,6 +159,16 @@ class Unauthenticated(HasPermission):
|
|
96
159
|
reason = "Authentication required"
|
97
160
|
|
98
161
|
|
162
|
+
class PreAuthenticated(HasPermission):
|
163
|
+
"""
|
164
|
+
Represent a permission check result that is not allowed due to
|
165
|
+
missing secondary authentication mechanism.
|
166
|
+
"""
|
167
|
+
|
168
|
+
kind = "mfa_required"
|
169
|
+
reason = "MFA required"
|
170
|
+
|
171
|
+
|
99
172
|
class Denied(HasPermission):
|
100
173
|
"""
|
101
174
|
Represent a permission check result that is not allowed due to lack of permission.
|
@@ -34,6 +34,8 @@ def check_permission(permission_name: str) -> CheckPermissionHook:
|
|
34
34
|
return
|
35
35
|
case "denied":
|
36
36
|
raise request.security_policy.Forbidden(detail=allowed.reason)
|
37
|
+
case "mfa_required":
|
38
|
+
raise request.security_policy.MFARequired(detail=allowed.reason)
|
37
39
|
case "unauthenticated":
|
38
40
|
raise request.security_policy.Unauthorized(detail=allowed.reason)
|
39
41
|
|
@@ -10,14 +10,16 @@ LocaleName = str
|
|
10
10
|
|
11
11
|
from fastlife.adapters.fastapi.request import GenericRequest # coverage: ignore
|
12
12
|
|
13
|
-
LocaleNegociator = Callable[
|
13
|
+
LocaleNegociator = Callable[
|
14
|
+
[GenericRequest[Any, Any, Any]], LocaleName
|
15
|
+
] # coverage: ignore
|
14
16
|
"""Interface to implement to negociate a locale""" # coverage: ignore
|
15
17
|
|
16
18
|
|
17
19
|
def default_negociator(settings: Settings) -> LocaleNegociator:
|
18
20
|
"""The default local negociator return the locale set in the conf."""
|
19
21
|
|
20
|
-
def locale_negociator(request: "GenericRequest[Any]") -> str:
|
22
|
+
def locale_negociator(request: "GenericRequest[Any, Any, Any]") -> str:
|
21
23
|
return settings.default_locale
|
22
24
|
|
23
25
|
return locale_negociator
|
@@ -2,34 +2,44 @@
|
|
2
2
|
|
3
3
|
import abc
|
4
4
|
from typing import Annotated, Any, Generic
|
5
|
-
from uuid import UUID
|
6
5
|
|
7
6
|
from fastapi import Depends
|
8
7
|
|
9
8
|
from fastlife import GenericRequest, get_request
|
10
9
|
from fastlife.domain.model.security_policy import (
|
11
10
|
Allowed,
|
11
|
+
Anonymous,
|
12
|
+
Authenticated,
|
13
|
+
AuthenticationState,
|
12
14
|
Forbidden,
|
13
15
|
HasPermission,
|
14
|
-
|
16
|
+
MFARequired,
|
17
|
+
PendingMFA,
|
18
|
+
TClaimedIdentity,
|
19
|
+
TIdentity,
|
15
20
|
Unauthorized,
|
16
21
|
)
|
17
22
|
from fastlife.service.registry import TRegistry
|
18
23
|
|
19
24
|
|
20
|
-
class AbstractSecurityPolicy(abc.ABC, Generic[
|
25
|
+
class AbstractSecurityPolicy(abc.ABC, Generic[TRegistry, TIdentity, TClaimedIdentity]):
|
21
26
|
"""Security policy base class."""
|
22
27
|
|
23
28
|
Forbidden = Forbidden
|
24
29
|
"""The exception raised if the user identified is not granted."""
|
25
30
|
Unauthorized = Unauthorized
|
26
31
|
"""The exception raised if no user has been identified."""
|
32
|
+
MFARequired = MFARequired
|
33
|
+
"""The exception raised if no user has been authenticated using a MFA."""
|
27
34
|
|
28
|
-
request: GenericRequest[TRegistry]
|
35
|
+
request: GenericRequest[TRegistry, TIdentity, TClaimedIdentity]
|
29
36
|
"""Request where the security policy is applied."""
|
30
37
|
|
31
38
|
def __init__(
|
32
|
-
self,
|
39
|
+
self,
|
40
|
+
request: Annotated[
|
41
|
+
GenericRequest[TRegistry, TIdentity, TClaimedIdentity], Depends(get_request)
|
42
|
+
],
|
33
43
|
):
|
34
44
|
"""
|
35
45
|
Build the security policy.
|
@@ -42,17 +52,48 @@ class AbstractSecurityPolicy(abc.ABC, Generic[TUser, TRegistry]):
|
|
42
52
|
"""
|
43
53
|
self.request = request
|
44
54
|
self.request.security_policy = self # we do backref to implement has_permission
|
55
|
+
self._authentication_state: (
|
56
|
+
AuthenticationState[TClaimedIdentity, TIdentity] | None
|
57
|
+
) = None
|
45
58
|
|
46
|
-
|
47
|
-
|
59
|
+
async def get_authentication_state(
|
60
|
+
self,
|
61
|
+
) -> AuthenticationState[TClaimedIdentity, TIdentity]:
|
62
|
+
"""
|
63
|
+
Return app-specific user object or None.
|
64
|
+
"""
|
65
|
+
if self._authentication_state is None:
|
66
|
+
self._authentication_state = await self.build_authentication_state()
|
67
|
+
return self._authentication_state
|
68
|
+
|
69
|
+
async def claimed_identity(self) -> TClaimedIdentity | None:
|
70
|
+
"""
|
71
|
+
Return app-specific user object that pretend to be identified.
|
72
|
+
"""
|
73
|
+
auth = await self.get_authentication_state()
|
74
|
+
match auth:
|
75
|
+
case PendingMFA(claimed):
|
76
|
+
return claimed
|
77
|
+
case _:
|
78
|
+
return None
|
79
|
+
|
80
|
+
async def identity(self) -> TIdentity | None:
|
48
81
|
"""
|
49
|
-
Return app-specific user object
|
82
|
+
Return app-specific user object after an mfa authentication or None.
|
50
83
|
"""
|
84
|
+
auth = await self.get_authentication_state()
|
85
|
+
match auth:
|
86
|
+
case Authenticated(identity):
|
87
|
+
return identity
|
88
|
+
case _:
|
89
|
+
return None
|
51
90
|
|
52
91
|
@abc.abstractmethod
|
53
|
-
async def
|
92
|
+
async def build_authentication_state(
|
93
|
+
self,
|
94
|
+
) -> AuthenticationState[TClaimedIdentity, TIdentity]:
|
54
95
|
"""
|
55
|
-
Return
|
96
|
+
Return the authentication state for the current request.
|
56
97
|
"""
|
57
98
|
|
58
99
|
@abc.abstractmethod
|
@@ -62,7 +103,11 @@ class AbstractSecurityPolicy(abc.ABC, Generic[TUser, TRegistry]):
|
|
62
103
|
"""Allow access to everything if signed in."""
|
63
104
|
|
64
105
|
@abc.abstractmethod
|
65
|
-
async def
|
106
|
+
async def pre_remember(self, claimed_identity: TClaimedIdentity) -> None:
|
107
|
+
"""Save the user identity in the request session."""
|
108
|
+
|
109
|
+
@abc.abstractmethod
|
110
|
+
async def remember(self, identity: TIdentity) -> None:
|
66
111
|
"""Save the user identity in the request session."""
|
67
112
|
|
68
113
|
@abc.abstractmethod
|
@@ -70,7 +115,12 @@ class AbstractSecurityPolicy(abc.ABC, Generic[TUser, TRegistry]):
|
|
70
115
|
"""Destroy the request session."""
|
71
116
|
|
72
117
|
|
73
|
-
class
|
118
|
+
class AbstractNoMFASecurityPolicy(AbstractSecurityPolicy[TRegistry, TIdentity, None]):
|
119
|
+
async def pre_remember(self, claimed_identity: None) -> None:
|
120
|
+
"""Do Nothing."""
|
121
|
+
|
122
|
+
|
123
|
+
class InsecurePolicy(AbstractNoMFASecurityPolicy[Any, None]):
|
74
124
|
"""
|
75
125
|
An implementation of the security policy made for explicit unsecured access.
|
76
126
|
|
@@ -79,13 +129,10 @@ class InsecurePolicy(AbstractSecurityPolicy[None, Any]):
|
|
79
129
|
or your own reason, the InsecurePolicy has to be set to the configurator.
|
80
130
|
"""
|
81
131
|
|
82
|
-
async def
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
async def authenticated_userid(self) -> str | UUID:
|
87
|
-
"""An uuid mades of 0."""
|
88
|
-
return UUID(int=0)
|
132
|
+
async def build_authentication_state(
|
133
|
+
self,
|
134
|
+
) -> AuthenticationState[None, None]:
|
135
|
+
return Anonymous
|
89
136
|
|
90
137
|
async def has_permission(
|
91
138
|
self, permission: str
|
@@ -93,7 +140,7 @@ class InsecurePolicy(AbstractSecurityPolicy[None, Any]):
|
|
93
140
|
"""Access is allways granted."""
|
94
141
|
return Allowed
|
95
142
|
|
96
|
-
async def remember(self,
|
143
|
+
async def remember(self, identity: None) -> None:
|
97
144
|
"""Do nothing."""
|
98
145
|
|
99
146
|
async def forget(self) -> None:
|
@@ -1,13 +1,13 @@
|
|
1
|
-
CHANGELOG.md,sha256=
|
2
|
-
fastlife/__init__.py,sha256=
|
1
|
+
CHANGELOG.md,sha256=Ap14kfVx07rFhITGDmtQfwEsJowg5osxKETE0w478lU,7801
|
2
|
+
fastlife/__init__.py,sha256=cx3BScbBelH-Tm63VPdAp5siW1fBZ0uOMwNW_SPs2xQ,2126
|
3
3
|
fastlife/adapters/__init__.py,sha256=imPD1hImpgrYkvUJRhHA5kVyGAua7VbP2WGkhSWKJT8,93
|
4
4
|
fastlife/adapters/fastapi/__init__.py,sha256=1goV1FGFP04TGyskJBLKZam4Gvt1yoAvLMNs4ekWSSQ,243
|
5
5
|
fastlife/adapters/fastapi/form.py,sha256=csxsDI6RK-g41pMwFhaVQCLDhF7dAZzgUp-VcrC3NFY,823
|
6
6
|
fastlife/adapters/fastapi/form_data.py,sha256=2DQ0o-RvY6iROUKQjS-UJdNYEVSsNPd-AjpergI3w54,4473
|
7
7
|
fastlife/adapters/fastapi/localizer.py,sha256=XD1kCJuAlkGevivmvAJEcGMCBWMef9rAfTOGmt3PVWU,436
|
8
|
-
fastlife/adapters/fastapi/request.py,sha256=
|
8
|
+
fastlife/adapters/fastapi/request.py,sha256=COOoSMZAm4VhyJgM7dlqJ7YdGjeGI7qs93PtBsriEPc,1115
|
9
9
|
fastlife/adapters/fastapi/routing/__init__.py,sha256=8EMnQE5n8oA4J9_c3nxzwKDVt3tefZ6fGH0d2owE8mo,195
|
10
|
-
fastlife/adapters/fastapi/routing/route.py,sha256=
|
10
|
+
fastlife/adapters/fastapi/routing/route.py,sha256=XnDPvd5V0Zl7Ke6bBErEtUCjmNQPcV2U_w1dWpx6qM4,1476
|
11
11
|
fastlife/adapters/fastapi/routing/router.py,sha256=jzrnU_Lyywu21e3spPaWQw8ujZh_Yy_EJOojcCi6ew4,499
|
12
12
|
fastlife/adapters/itsdangerous/__init__.py,sha256=7ocGY7v0cxooZBKQYjA2JkmzRqiBvcU1uzA84UsTVAI,84
|
13
13
|
fastlife/adapters/itsdangerous/session.py,sha256=9h_WRsXqZbytHZOv5B_K3OWD5mbfYzxHulXoOf6D2MI,1685
|
@@ -1686,7 +1686,7 @@ fastlife/components/pydantic_form/FatalError.jinja,sha256=lFVlNrXzBR6ExMahq77h0t
|
|
1686
1686
|
fastlife/components/pydantic_form/Hint.jinja,sha256=8leBpfMGDmalc_KAjr2paTojr_rwq-luS6m_1BGj7Tw,202
|
1687
1687
|
fastlife/components/pydantic_form/Widget.jinja,sha256=PgguUpvhG6CY9AW6H8qQMjKqjlybjDCAaFFAOHzrzVQ,418
|
1688
1688
|
fastlife/config/__init__.py,sha256=5qpuaVYqi-AS0GgsfggM6rFsSwXgrqrLBo9jH6dVroc,407
|
1689
|
-
fastlife/config/configurator.py,sha256=
|
1689
|
+
fastlife/config/configurator.py,sha256=SURXmBrdTghHoG2f9R2BUF6TKZXg1lNmwP3ZbuApJ3M,24722
|
1690
1690
|
fastlife/config/exceptions.py,sha256=9MdBnbfy-Aw-KaIFzju0Kh8Snk41-v9LqK2w48Tdy1s,1169
|
1691
1691
|
fastlife/config/openapiextra.py,sha256=rYoerrn9sni2XwnO3gIWqaz7M0aDZPhVLjzqhDxue0o,514
|
1692
1692
|
fastlife/config/resources.py,sha256=u6OgnbHfGkC5idH-YPNkIPf8GJnZpJoGVZ-Ym022BCo,8533
|
@@ -1696,8 +1696,8 @@ fastlife/domain/model/__init__.py,sha256=aoBjaSpDscuFXvtknJHwiNyoJRUpE-v4X54h_wN
|
|
1696
1696
|
fastlife/domain/model/asgi.py,sha256=RSTnfTsofOmCaWzHNuRGowjlyHYmoDCrXFbvNY_B55k,129
|
1697
1697
|
fastlife/domain/model/csrf.py,sha256=BUiWK-S7rVciWHO1qTkM8e_KxzpF6gGC4MMJK1v6iDo,414
|
1698
1698
|
fastlife/domain/model/form.py,sha256=WriBT1qUUIbf5x5iewo9ChEcr6k0en8jMTD0iaei5Pk,3253
|
1699
|
-
fastlife/domain/model/request.py,sha256=
|
1700
|
-
fastlife/domain/model/security_policy.py,sha256=
|
1699
|
+
fastlife/domain/model/request.py,sha256=ZRHZW_MOmtO_DFHt2UYu_aUmtoMdD14085A8Z8_eS8s,2678
|
1700
|
+
fastlife/domain/model/security_policy.py,sha256=f9SLi54vvRU-KSPJ5K0unoqYpkxIyzuZjKf2Ylwf5Rg,4796
|
1701
1701
|
fastlife/domain/model/template.py,sha256=z9oxdKme1hMPuvk7mBiKR_tuVY8TqH77aTYqMgvEGl8,876
|
1702
1702
|
fastlife/domain/model/types.py,sha256=64jJKFAi5x0e3vr8naHU1m_as0Qy8MS-s9CG0z6K1qc,381
|
1703
1703
|
fastlife/middlewares/__init__.py,sha256=C3DUOzR5EhlAv5Zq7h-Abyvkd7bUsJohTRSB2wpRYQE,220
|
@@ -1709,11 +1709,11 @@ fastlife/middlewares/session/middleware.py,sha256=ituZ5hNipDMkgCXNE4zbnmOcWEF151
|
|
1709
1709
|
fastlife/middlewares/session/serializer.py,sha256=nbJGiCJ_ryZxkW1I28kmK6hD3U98D4ZlUQA7B8_tngQ,635
|
1710
1710
|
fastlife/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1711
1711
|
fastlife/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1712
|
-
fastlife/service/check_permission.py,sha256=-
|
1712
|
+
fastlife/service/check_permission.py,sha256=-TsI58YZJtWIw5bsm0fVpfuaCMUx4cmoLTKGXeyQPDk,1809
|
1713
1713
|
fastlife/service/csrf.py,sha256=wC1PaKOmZ3il0FF_kevxnlg9PxDqruRdLrNnOA3ZHrU,1886
|
1714
|
-
fastlife/service/locale_negociator.py,sha256=
|
1714
|
+
fastlife/service/locale_negociator.py,sha256=JUqzTukxDqTJVOR-CNI7Vqo6kvdvwxYvZQe8P3V9S2U,796
|
1715
1715
|
fastlife/service/registry.py,sha256=B6n5b_b0RgxJj0qFOpnrJFmG7_MPtvShwV6yH9V6vi0,2098
|
1716
|
-
fastlife/service/security_policy.py,sha256=
|
1716
|
+
fastlife/service/security_policy.py,sha256=qYXs4mhfz_u4x59NhUkirqKYKQbFv9YrzyRuXj7mxE0,4688
|
1717
1717
|
fastlife/service/templates.py,sha256=QPAIUbbZiekazz_jV3q4JCwQd6Q4KA6a4RDek2RWuhE,2548
|
1718
1718
|
fastlife/service/translations.py,sha256=D-1D3pVNytEcps1u-0K7FmgQ8Wo6Yu4XVHvZrPhBmAI,5795
|
1719
1719
|
fastlife/settings.py,sha256=q-rz4CEF2RQGow5-m-yZJOvdh3PPb2c1Q_ZLJGnu4VQ,3647
|
@@ -1728,9 +1728,9 @@ fastlife/testing/session.py,sha256=LEFFbiR67_x_g-ioudkY0C7PycHdbDfaIaoo_G7GXQ8,2
|
|
1728
1728
|
fastlife/testing/testclient.py,sha256=JTIgeMKooA8L4gEodeC3gy4Lo27y3WNswSEIKLlVVPs,6745
|
1729
1729
|
fastlife/views/__init__.py,sha256=zG8gveL8e2zBdYx6_9jtZfpQ6qJT-MFnBY3xXkLwHZI,22
|
1730
1730
|
fastlife/views/pydantic_form.py,sha256=o7EUItciAGL1OSaGNHo-3BTrYAk34GuWE7zGikjiAGY,1486
|
1731
|
-
fastlifeweb-0.
|
1732
|
-
fastlifeweb-0.
|
1733
|
-
fastlifeweb-0.
|
1734
|
-
fastlifeweb-0.
|
1731
|
+
fastlifeweb-0.23.0.dist-info/METADATA,sha256=Eww8hBxH7oR5_EeqtALpdmUmJr2f1vs1E0EijXaxM30,3663
|
1732
|
+
fastlifeweb-0.23.0.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
1733
|
+
fastlifeweb-0.23.0.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
1734
|
+
fastlifeweb-0.23.0.dist-info/licenses/LICENSE,sha256=NlRX9Z-dcv8X1VFW9odlIQBbgNN9pcO94XzvKp2R16o,1075
|
1735
1735
|
tailwind.config.js,sha256=EN3EahBDmQBbmJvkw3SdGWNOkfkzw0cg-QvBikOhkrw,1348
|
1736
|
-
fastlifeweb-0.
|
1736
|
+
fastlifeweb-0.23.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|