plain.auth 0.19.0__py3-none-any.whl → 0.20.1__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.
- plain/auth/CHANGELOG.md +26 -0
- plain/auth/README.md +16 -13
- plain/auth/__init__.py +3 -2
- plain/auth/requests.py +38 -0
- plain/auth/sessions.py +30 -22
- plain/auth/templates.py +14 -0
- plain/auth/test.py +14 -7
- plain/auth/views.py +51 -26
- {plain_auth-0.19.0.dist-info → plain_auth-0.20.1.dist-info}/METADATA +17 -14
- plain_auth-0.20.1.dist-info/RECORD +14 -0
- plain/auth/middleware.py +0 -33
- plain_auth-0.19.0.dist-info/RECORD +0 -13
- {plain_auth-0.19.0.dist-info → plain_auth-0.20.1.dist-info}/WHEEL +0 -0
- {plain_auth-0.19.0.dist-info → plain_auth-0.20.1.dist-info}/licenses/LICENSE +0 -0
plain/auth/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# plain-auth changelog
|
|
2
2
|
|
|
3
|
+
## [0.20.1](https://github.com/dropseed/plain/releases/plain-auth@0.20.1) (2025-10-02)
|
|
4
|
+
|
|
5
|
+
### What's changed
|
|
6
|
+
|
|
7
|
+
- Updated README documentation to use `get_request_user()` and `get_current_user()` instead of `request.user` ([f6278d9](https://github.com/dropseed/plain/commit/f6278d9bb4))
|
|
8
|
+
|
|
9
|
+
### Upgrade instructions
|
|
10
|
+
|
|
11
|
+
- No changes required
|
|
12
|
+
|
|
13
|
+
## [0.20.0](https://github.com/dropseed/plain/releases/plain-auth@0.20.0) (2025-10-02)
|
|
14
|
+
|
|
15
|
+
### What's changed
|
|
16
|
+
|
|
17
|
+
- Removed `AuthenticationMiddleware` - authentication is now handled through request-based functions instead of middleware ([154ee10](https://github.com/dropseed/plain/commit/154ee10))
|
|
18
|
+
- Replaced `request.user` attribute with `get_request_user(request)` function and `{{ get_current_user() }}` template global ([154ee10](https://github.com/dropseed/plain/commit/154ee10))
|
|
19
|
+
- `AuthViewMixin` now provides a `self.user` property for accessing the authenticated user in views ([154ee10](https://github.com/dropseed/plain/commit/154ee10))
|
|
20
|
+
- Renamed `get_user` to `get_request_user` in the public API ([154ee10](https://github.com/dropseed/plain/commit/154ee10))
|
|
21
|
+
|
|
22
|
+
### Upgrade instructions
|
|
23
|
+
|
|
24
|
+
- Remove `plain.auth.middleware.AuthenticationMiddleware` from your `MIDDLEWARE` setting
|
|
25
|
+
- In views, use `AuthViewMixin` for access to `self.user` instead of `self.request.user`
|
|
26
|
+
- Replace `request.user` with `get_request_user(request)` in code outside of `AuthViewMixin` views
|
|
27
|
+
- In templates, replace `{{ request.user }}` with `{{ user }}` (from `AuthViewMixin`) or with `{{ get_current_user() }}`
|
|
28
|
+
|
|
3
29
|
## [0.19.0](https://github.com/dropseed/plain/releases/plain-auth@0.19.0) (2025-09-30)
|
|
4
30
|
|
|
5
31
|
### What's changed
|
plain/auth/README.md
CHANGED
|
@@ -17,8 +17,11 @@ The `plain.auth` package provides user authentication and authorization for Plai
|
|
|
17
17
|
|
|
18
18
|
```python
|
|
19
19
|
# In a view
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
from plain.auth import get_request_user
|
|
21
|
+
|
|
22
|
+
user = get_request_user(request)
|
|
23
|
+
if user:
|
|
24
|
+
print(f"Hello, {user.email}!")
|
|
22
25
|
else:
|
|
23
26
|
print("You are not logged in.")
|
|
24
27
|
```
|
|
@@ -33,7 +36,7 @@ class ProfileView(AuthViewMixin, View):
|
|
|
33
36
|
login_required = True
|
|
34
37
|
|
|
35
38
|
def get(self):
|
|
36
|
-
return f"Welcome, {self.
|
|
39
|
+
return f"Welcome, {self.user.email}!"
|
|
37
40
|
```
|
|
38
41
|
|
|
39
42
|
## Authentication setup
|
|
@@ -52,7 +55,6 @@ INSTALLED_PACKAGES = [
|
|
|
52
55
|
|
|
53
56
|
MIDDLEWARE = [
|
|
54
57
|
"plain.sessions.middleware.SessionMiddleware",
|
|
55
|
-
"plain.auth.middleware.AuthenticationMiddleware",
|
|
56
58
|
]
|
|
57
59
|
|
|
58
60
|
AUTH_USER_MODEL = "users.User"
|
|
@@ -109,23 +111,24 @@ urlpatterns = [
|
|
|
109
111
|
|
|
110
112
|
## Checking if a user is logged in
|
|
111
113
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
In templates:
|
|
114
|
+
In templates, use the `get_current_user()` function:
|
|
115
115
|
|
|
116
116
|
```html
|
|
117
|
-
{% if
|
|
118
|
-
<p>Hello, {{
|
|
117
|
+
{% if get_current_user() %}
|
|
118
|
+
<p>Hello, {{ get_current_user().email }}!</p>
|
|
119
119
|
{% else %}
|
|
120
120
|
<p>You are not logged in.</p>
|
|
121
121
|
{% endif %}
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
-
In Python code
|
|
124
|
+
In Python code, use `get_request_user()`:
|
|
125
125
|
|
|
126
126
|
```python
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
from plain.auth import get_request_user
|
|
128
|
+
|
|
129
|
+
user = get_request_user(request)
|
|
130
|
+
if user:
|
|
131
|
+
print(f"Hello, {user.email}!")
|
|
129
132
|
else:
|
|
130
133
|
print("You are not logged in.")
|
|
131
134
|
```
|
|
@@ -152,7 +155,7 @@ class AdminOnlyView(AuthViewMixin, View):
|
|
|
152
155
|
class CustomPermissionView(AuthViewMixin, View):
|
|
153
156
|
def check_auth(self):
|
|
154
157
|
super().check_auth()
|
|
155
|
-
if not self.
|
|
158
|
+
if not self.user.is_special:
|
|
156
159
|
raise PermissionDenied("You're not special!")
|
|
157
160
|
```
|
|
158
161
|
|
plain/auth/__init__.py
CHANGED
plain/auth/requests.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
from weakref import WeakKeyDictionary
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from plain.http import Request
|
|
8
|
+
|
|
9
|
+
from .sessions import get_user_model
|
|
10
|
+
|
|
11
|
+
User = get_user_model()
|
|
12
|
+
|
|
13
|
+
_request_users: WeakKeyDictionary[Request, User | None] = WeakKeyDictionary()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def set_request_user(request: Request, user: User | None) -> None:
|
|
17
|
+
"""Store the authenticated user for this request."""
|
|
18
|
+
_request_users[request] = user
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_request_user(request: Request) -> User | None:
|
|
22
|
+
"""
|
|
23
|
+
Get the authenticated user for this request, if any.
|
|
24
|
+
|
|
25
|
+
Lazily loads the user from the session on first access.
|
|
26
|
+
"""
|
|
27
|
+
if request not in _request_users:
|
|
28
|
+
from .sessions import get_user
|
|
29
|
+
|
|
30
|
+
user = get_user(request)
|
|
31
|
+
|
|
32
|
+
# Don't need to store a bunch of None values
|
|
33
|
+
if not user:
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
_request_users[request] = user
|
|
37
|
+
|
|
38
|
+
return _request_users[request]
|
plain/auth/sessions.py
CHANGED
|
@@ -3,9 +3,12 @@ import hmac
|
|
|
3
3
|
from plain.exceptions import ImproperlyConfigured
|
|
4
4
|
from plain.models import models_registry
|
|
5
5
|
from plain.runtime import settings
|
|
6
|
+
from plain.sessions import get_request_session
|
|
6
7
|
from plain.utils.crypto import salted_hmac
|
|
7
8
|
from plain.utils.encoding import force_bytes
|
|
8
9
|
|
|
10
|
+
from .requests import get_request_user, set_request_user
|
|
11
|
+
|
|
9
12
|
USER_ID_SESSION_KEY = "_auth_user_id"
|
|
10
13
|
USER_HASH_SESSION_KEY = "_auth_user_hash"
|
|
11
14
|
|
|
@@ -26,9 +29,11 @@ def update_session_auth_hash(request, user):
|
|
|
26
29
|
prevent a password change from logging out the session from which the
|
|
27
30
|
password was changed.
|
|
28
31
|
"""
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
|
|
33
|
+
session = get_request_session(request)
|
|
34
|
+
session.cycle_key()
|
|
35
|
+
if get_request_user(request) == user:
|
|
36
|
+
session[USER_HASH_SESSION_KEY] = get_session_auth_hash(user)
|
|
32
37
|
|
|
33
38
|
|
|
34
39
|
def get_session_auth_fallback_hash(user):
|
|
@@ -52,33 +57,34 @@ def login(request, user):
|
|
|
52
57
|
have to reauthenticate on every request. Note that data set during
|
|
53
58
|
the anonymous session is retained when the user logs in.
|
|
54
59
|
"""
|
|
60
|
+
session = get_request_session(request)
|
|
61
|
+
|
|
55
62
|
if settings.AUTH_USER_SESSION_HASH_FIELD:
|
|
56
63
|
session_auth_hash = get_session_auth_hash(user)
|
|
57
64
|
else:
|
|
58
65
|
session_auth_hash = ""
|
|
59
66
|
|
|
60
|
-
if USER_ID_SESSION_KEY in
|
|
61
|
-
if int(
|
|
67
|
+
if USER_ID_SESSION_KEY in session:
|
|
68
|
+
if int(session[USER_ID_SESSION_KEY]) != user.id:
|
|
62
69
|
# To avoid reusing another user's session, create a new, empty
|
|
63
70
|
# session if the existing session corresponds to a different
|
|
64
71
|
# authenticated user.
|
|
65
|
-
|
|
72
|
+
session.flush()
|
|
66
73
|
elif session_auth_hash and not hmac.compare_digest(
|
|
67
|
-
force_bytes(
|
|
74
|
+
force_bytes(session.get(USER_HASH_SESSION_KEY, "")),
|
|
68
75
|
force_bytes(session_auth_hash),
|
|
69
76
|
):
|
|
70
77
|
# If the session hash does not match the current hash, reset the
|
|
71
78
|
# session. Most likely this means the password was changed.
|
|
72
|
-
|
|
79
|
+
session.flush()
|
|
73
80
|
else:
|
|
74
81
|
# Invalidate the current session key and generate a new one to enhance security,
|
|
75
82
|
# typically done after user login to prevent session fixation attacks.
|
|
76
|
-
|
|
83
|
+
session.cycle_key()
|
|
77
84
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
request.user = user
|
|
85
|
+
session[USER_ID_SESSION_KEY] = user.id
|
|
86
|
+
session[USER_HASH_SESSION_KEY] = session_auth_hash
|
|
87
|
+
set_request_user(request, user)
|
|
82
88
|
|
|
83
89
|
|
|
84
90
|
def logout(request):
|
|
@@ -88,9 +94,9 @@ def logout(request):
|
|
|
88
94
|
"""
|
|
89
95
|
# Dispatch the signal before the user is logged out so the receivers have a
|
|
90
96
|
# chance to find out *who* logged out.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
session = get_request_session(request)
|
|
98
|
+
session.flush()
|
|
99
|
+
set_request_user(request, None)
|
|
94
100
|
|
|
95
101
|
|
|
96
102
|
def get_user_model():
|
|
@@ -114,12 +120,14 @@ def get_user(request):
|
|
|
114
120
|
Return the user model instance associated with the given request session.
|
|
115
121
|
If no user is retrieved, return None.
|
|
116
122
|
"""
|
|
117
|
-
|
|
123
|
+
session = get_request_session(request)
|
|
124
|
+
|
|
125
|
+
if USER_ID_SESSION_KEY not in session:
|
|
118
126
|
return None
|
|
119
127
|
|
|
120
128
|
UserModel = get_user_model()
|
|
121
129
|
try:
|
|
122
|
-
user = UserModel.query.get(id=
|
|
130
|
+
user = UserModel.query.get(id=session[USER_ID_SESSION_KEY])
|
|
123
131
|
except UserModel.DoesNotExist:
|
|
124
132
|
return None
|
|
125
133
|
|
|
@@ -130,7 +138,7 @@ def get_user(request):
|
|
|
130
138
|
# If it has changed (i.e. password changed), then the session
|
|
131
139
|
# is no longer valid and cleared out.
|
|
132
140
|
if settings.AUTH_USER_SESSION_HASH_FIELD:
|
|
133
|
-
session_hash =
|
|
141
|
+
session_hash = session.get(USER_HASH_SESSION_KEY)
|
|
134
142
|
if not session_hash:
|
|
135
143
|
session_hash_verified = False
|
|
136
144
|
else:
|
|
@@ -148,10 +156,10 @@ def get_user(request):
|
|
|
148
156
|
)
|
|
149
157
|
for fallback_auth_hash in get_session_auth_fallback_hash(user)
|
|
150
158
|
):
|
|
151
|
-
|
|
152
|
-
|
|
159
|
+
session.cycle_key()
|
|
160
|
+
session[USER_HASH_SESSION_KEY] = session_auth_hash
|
|
153
161
|
else:
|
|
154
|
-
|
|
162
|
+
session.flush()
|
|
155
163
|
user = None
|
|
156
164
|
|
|
157
165
|
return user
|
plain/auth/templates.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from jinja2 import pass_context
|
|
2
|
+
|
|
3
|
+
from plain.templates import register_template_global
|
|
4
|
+
|
|
5
|
+
from .requests import get_request_user
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@register_template_global
|
|
9
|
+
@pass_context
|
|
10
|
+
def get_current_user(context):
|
|
11
|
+
"""Get the authenticated user for the current request."""
|
|
12
|
+
request = context.get("request")
|
|
13
|
+
assert request is not None, "No request in template context"
|
|
14
|
+
return get_request_user(request)
|
plain/auth/test.py
CHANGED
|
@@ -3,7 +3,9 @@ from http.cookies import SimpleCookie
|
|
|
3
3
|
from plain.http.request import Request
|
|
4
4
|
from plain.runtime import settings
|
|
5
5
|
from plain.sessions import SessionStore
|
|
6
|
+
from plain.sessions.requests import get_request_session, set_request_session
|
|
6
7
|
|
|
8
|
+
from .requests import set_request_user
|
|
7
9
|
from .sessions import get_user, login, logout
|
|
8
10
|
|
|
9
11
|
|
|
@@ -11,13 +13,15 @@ def login_client(client, user):
|
|
|
11
13
|
"""Log a user into a test client."""
|
|
12
14
|
request = Request()
|
|
13
15
|
if client.session:
|
|
14
|
-
|
|
16
|
+
session = client.session
|
|
15
17
|
else:
|
|
16
|
-
|
|
18
|
+
session = SessionStore()
|
|
19
|
+
set_request_session(request, session)
|
|
17
20
|
login(request, user)
|
|
18
|
-
|
|
21
|
+
session = get_request_session(request)
|
|
22
|
+
session.save()
|
|
19
23
|
session_cookie = settings.SESSION_COOKIE_NAME
|
|
20
|
-
client.cookies[session_cookie] =
|
|
24
|
+
client.cookies[session_cookie] = session.session_key
|
|
21
25
|
cookie_data = {
|
|
22
26
|
"max-age": None,
|
|
23
27
|
"path": "/",
|
|
@@ -32,9 +36,12 @@ def logout_client(client):
|
|
|
32
36
|
"""Log out a user from a test client."""
|
|
33
37
|
request = Request()
|
|
34
38
|
if client.session:
|
|
35
|
-
|
|
36
|
-
request
|
|
39
|
+
session = client.session
|
|
40
|
+
set_request_session(request, session)
|
|
41
|
+
user = get_user(request)
|
|
42
|
+
set_request_user(request, user)
|
|
37
43
|
else:
|
|
38
|
-
|
|
44
|
+
session = SessionStore()
|
|
45
|
+
set_request_session(request, session)
|
|
39
46
|
logout(request)
|
|
40
47
|
client.cookies = SimpleCookie()
|
plain/auth/views.py
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from functools import cached_property
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
1
5
|
from urllib.parse import urlparse, urlunparse
|
|
2
6
|
|
|
3
7
|
from plain.exceptions import PermissionDenied
|
|
@@ -8,6 +12,7 @@ from plain.http import (
|
|
|
8
12
|
ResponseRedirect,
|
|
9
13
|
)
|
|
10
14
|
from plain.runtime import settings
|
|
15
|
+
from plain.sessions.views import SessionViewMixin
|
|
11
16
|
from plain.urls import reverse
|
|
12
17
|
from plain.utils.cache import patch_cache_control
|
|
13
18
|
from plain.views import View
|
|
@@ -15,6 +20,13 @@ from plain.views import View
|
|
|
15
20
|
from .sessions import logout
|
|
16
21
|
from .utils import resolve_url
|
|
17
22
|
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from plain.http import Request
|
|
25
|
+
|
|
26
|
+
from .sessions import get_user_model
|
|
27
|
+
|
|
28
|
+
User = get_user_model()
|
|
29
|
+
|
|
18
30
|
|
|
19
31
|
class LoginRequired(Exception):
|
|
20
32
|
def __init__(self, login_url=None, redirect_field_name="next"):
|
|
@@ -22,43 +34,53 @@ class LoginRequired(Exception):
|
|
|
22
34
|
self.redirect_field_name = redirect_field_name
|
|
23
35
|
|
|
24
36
|
|
|
25
|
-
class AuthViewMixin:
|
|
26
|
-
login_required =
|
|
27
|
-
admin_required = False
|
|
37
|
+
class AuthViewMixin(SessionViewMixin):
|
|
38
|
+
login_required = False
|
|
39
|
+
admin_required = False # Implies login_required
|
|
28
40
|
login_url = settings.AUTH_LOGIN_URL
|
|
29
41
|
|
|
42
|
+
request: Request
|
|
43
|
+
|
|
44
|
+
@cached_property
|
|
45
|
+
def user(self) -> User | None:
|
|
46
|
+
"""Get the authenticated user for this request."""
|
|
47
|
+
from .requests import get_request_user
|
|
48
|
+
|
|
49
|
+
return get_request_user(self.request)
|
|
50
|
+
|
|
51
|
+
def get_template_context(self) -> dict:
|
|
52
|
+
"""Add user and impersonator to template context."""
|
|
53
|
+
context = super().get_template_context()
|
|
54
|
+
context["user"] = self.user
|
|
55
|
+
return context
|
|
56
|
+
|
|
30
57
|
def check_auth(self) -> None:
|
|
31
58
|
"""
|
|
32
59
|
Raises either LoginRequired or PermissionDenied.
|
|
33
60
|
- LoginRequired can specify a login_url and redirect_field_name
|
|
34
61
|
- PermissionDenied can specify a message
|
|
35
62
|
"""
|
|
63
|
+
if not self.login_required and not self.admin_required:
|
|
64
|
+
return None
|
|
36
65
|
|
|
37
|
-
if not
|
|
38
|
-
raise AttributeError(
|
|
39
|
-
"AuthViewMixin requires the request attribute to be set."
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
if self.login_required and not self.request.user:
|
|
66
|
+
if not self.user:
|
|
43
67
|
raise LoginRequired(login_url=self.login_url)
|
|
44
68
|
|
|
45
|
-
if
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
if
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
69
|
+
if self.admin_required:
|
|
70
|
+
# At this point, we know user is authenticated (from check above)
|
|
71
|
+
# Check if impersonation is active
|
|
72
|
+
if impersonator := getattr(self, "impersonator", None):
|
|
73
|
+
# Impersonators should be able to view admin pages while impersonating.
|
|
74
|
+
# There's probably never a case where an impersonator isn't admin, but it can be configured.
|
|
75
|
+
if not impersonator.is_admin:
|
|
76
|
+
raise PermissionDenied(
|
|
77
|
+
"You do not have permission to access this page."
|
|
78
|
+
)
|
|
79
|
+
elif not self.user.is_admin:
|
|
80
|
+
# Show a 404 so we don't expose admin urls to non-admin users
|
|
81
|
+
raise Http404()
|
|
55
82
|
|
|
56
83
|
def get_response(self) -> Response:
|
|
57
|
-
if not hasattr(self, "request"):
|
|
58
|
-
raise AttributeError(
|
|
59
|
-
"AuthViewMixin requires the request attribute to be set."
|
|
60
|
-
)
|
|
61
|
-
|
|
62
84
|
try:
|
|
63
85
|
self.check_auth()
|
|
64
86
|
except LoginRequired as e:
|
|
@@ -85,8 +107,11 @@ class AuthViewMixin:
|
|
|
85
107
|
raise PermissionDenied("Login required")
|
|
86
108
|
|
|
87
109
|
response = super().get_response()
|
|
88
|
-
|
|
89
|
-
|
|
110
|
+
|
|
111
|
+
if self.user:
|
|
112
|
+
# Make sure it at least has private as a default
|
|
113
|
+
patch_cache_control(response, private=True)
|
|
114
|
+
|
|
90
115
|
return response
|
|
91
116
|
|
|
92
117
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plain.auth
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.20.1
|
|
4
4
|
Summary: Add users to your app and decide what they can access.
|
|
5
5
|
Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -29,8 +29,11 @@ The `plain.auth` package provides user authentication and authorization for Plai
|
|
|
29
29
|
|
|
30
30
|
```python
|
|
31
31
|
# In a view
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
from plain.auth import get_request_user
|
|
33
|
+
|
|
34
|
+
user = get_request_user(request)
|
|
35
|
+
if user:
|
|
36
|
+
print(f"Hello, {user.email}!")
|
|
34
37
|
else:
|
|
35
38
|
print("You are not logged in.")
|
|
36
39
|
```
|
|
@@ -45,7 +48,7 @@ class ProfileView(AuthViewMixin, View):
|
|
|
45
48
|
login_required = True
|
|
46
49
|
|
|
47
50
|
def get(self):
|
|
48
|
-
return f"Welcome, {self.
|
|
51
|
+
return f"Welcome, {self.user.email}!"
|
|
49
52
|
```
|
|
50
53
|
|
|
51
54
|
## Authentication setup
|
|
@@ -64,7 +67,6 @@ INSTALLED_PACKAGES = [
|
|
|
64
67
|
|
|
65
68
|
MIDDLEWARE = [
|
|
66
69
|
"plain.sessions.middleware.SessionMiddleware",
|
|
67
|
-
"plain.auth.middleware.AuthenticationMiddleware",
|
|
68
70
|
]
|
|
69
71
|
|
|
70
72
|
AUTH_USER_MODEL = "users.User"
|
|
@@ -121,23 +123,24 @@ urlpatterns = [
|
|
|
121
123
|
|
|
122
124
|
## Checking if a user is logged in
|
|
123
125
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
In templates:
|
|
126
|
+
In templates, use the `get_current_user()` function:
|
|
127
127
|
|
|
128
128
|
```html
|
|
129
|
-
{% if
|
|
130
|
-
<p>Hello, {{
|
|
129
|
+
{% if get_current_user() %}
|
|
130
|
+
<p>Hello, {{ get_current_user().email }}!</p>
|
|
131
131
|
{% else %}
|
|
132
132
|
<p>You are not logged in.</p>
|
|
133
133
|
{% endif %}
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
In Python code
|
|
136
|
+
In Python code, use `get_request_user()`:
|
|
137
137
|
|
|
138
138
|
```python
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
from plain.auth import get_request_user
|
|
140
|
+
|
|
141
|
+
user = get_request_user(request)
|
|
142
|
+
if user:
|
|
143
|
+
print(f"Hello, {user.email}!")
|
|
141
144
|
else:
|
|
142
145
|
print("You are not logged in.")
|
|
143
146
|
```
|
|
@@ -164,7 +167,7 @@ class AdminOnlyView(AuthViewMixin, View):
|
|
|
164
167
|
class CustomPermissionView(AuthViewMixin, View):
|
|
165
168
|
def check_auth(self):
|
|
166
169
|
super().check_auth()
|
|
167
|
-
if not self.
|
|
170
|
+
if not self.user.is_special:
|
|
168
171
|
raise PermissionDenied("You're not special!")
|
|
169
172
|
```
|
|
170
173
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
plain/auth/CHANGELOG.md,sha256=W74wFZKot-9heq8l_MsoLSDmHLdAe5LSo9qFPliXFS0,4533
|
|
2
|
+
plain/auth/README.md,sha256=I1SeOyrBnF0GAjD7T0k5OlZ2bSHz9---nElinaobewc,4030
|
|
3
|
+
plain/auth/__init__.py,sha256=CrOsS74CPGN1nPTTfie13mPgdyVLRyZ1YwDPIA77uaA,179
|
|
4
|
+
plain/auth/default_settings.py,sha256=65VzDn3j61OMn78Lg6Zuds4A8QKzJJ_0G9KoFqAOIRo,466
|
|
5
|
+
plain/auth/requests.py,sha256=f8QUpqHueoTYnL2ITgRXxLFYLSxra9jW3p9pjFgj0B0,949
|
|
6
|
+
plain/auth/sessions.py,sha256=Pz27K2wFfij0H0nGg_2rq0WJpcgTWW22bG0YvHM-dfc,5679
|
|
7
|
+
plain/auth/templates.py,sha256=MQ9vdxE2fBXN-F043g8EGm93s6HRvEMtGHrFcbAr3Fs,400
|
|
8
|
+
plain/auth/test.py,sha256=bLxQEp1mM4t9b-bSSq2u9_Z07Wkl--7U_mSBb9VV7BU,1428
|
|
9
|
+
plain/auth/utils.py,sha256=eEON0Mo928l-aW5tqBuoTdVke8aP4majxVtAFoLroSE,1280
|
|
10
|
+
plain/auth/views.py,sha256=Gm91sB_Z47h-8SwxCMKVKl57FSjed_AM5fdeaiPmz-E,4761
|
|
11
|
+
plain_auth-0.20.1.dist-info/METADATA,sha256=5E0uGbf7uFA-F0ywFO45xQOLQSCbvYw9nSNVyezRITI,4390
|
|
12
|
+
plain_auth-0.20.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
13
|
+
plain_auth-0.20.1.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
|
|
14
|
+
plain_auth-0.20.1.dist-info/RECORD,,
|
plain/auth/middleware.py
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
from opentelemetry import trace
|
|
2
|
-
from opentelemetry.semconv._incubating.attributes.user_attributes import USER_ID
|
|
3
|
-
|
|
4
|
-
from plain import auth
|
|
5
|
-
from plain.exceptions import ImproperlyConfigured
|
|
6
|
-
from plain.utils.functional import SimpleLazyObject
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def get_user(request):
|
|
10
|
-
if not hasattr(request, "_cached_user"):
|
|
11
|
-
request._cached_user = auth.get_user(request)
|
|
12
|
-
if request._cached_user:
|
|
13
|
-
trace.get_current_span().set_attribute(USER_ID, request._cached_user.id)
|
|
14
|
-
return request._cached_user
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class AuthenticationMiddleware:
|
|
18
|
-
def __init__(self, get_response):
|
|
19
|
-
self.get_response = get_response
|
|
20
|
-
|
|
21
|
-
def __call__(self, request):
|
|
22
|
-
if not hasattr(request, "session"):
|
|
23
|
-
raise ImproperlyConfigured(
|
|
24
|
-
"The Plain authentication middleware requires session "
|
|
25
|
-
"middleware to be installed. Edit your MIDDLEWARE setting to "
|
|
26
|
-
"insert "
|
|
27
|
-
"'plain.sessions.middleware.SessionMiddleware' before "
|
|
28
|
-
"'plain.auth.middleware.AuthenticationMiddleware'."
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
request.user = SimpleLazyObject(lambda: get_user(request))
|
|
32
|
-
|
|
33
|
-
return self.get_response(request)
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
plain/auth/CHANGELOG.md,sha256=ViLkVHkmNYuFEAeTUQzrS9QxSIPAuo7QS7Mr7LPXNnk,3009
|
|
2
|
-
plain/auth/README.md,sha256=zPG1AY8pTREu0clHVZDul1fdnv2eqXhMOdwxlnE85sI,3998
|
|
3
|
-
plain/auth/__init__.py,sha256=Id4ON6caLuZoZhu_kL6aMrwnN3Y8gPGVrVGs_V3ofAE,142
|
|
4
|
-
plain/auth/default_settings.py,sha256=65VzDn3j61OMn78Lg6Zuds4A8QKzJJ_0G9KoFqAOIRo,466
|
|
5
|
-
plain/auth/middleware.py,sha256=yiuJ6jg4PW5FpGFEzOzg5SLTjcheRTVPvo4NlmeBKws,1186
|
|
6
|
-
plain/auth/sessions.py,sha256=yd9iBOrfSvqo-Q5352Yb78WTY0Q38rJ1988UgUmJw3o,5572
|
|
7
|
-
plain/auth/test.py,sha256=tPAVQfKxbbqUMjQM_sTIXTNWY-xaYEuboOvygf-jUTo,1151
|
|
8
|
-
plain/auth/utils.py,sha256=eEON0Mo928l-aW5tqBuoTdVke8aP4majxVtAFoLroSE,1280
|
|
9
|
-
plain/auth/views.py,sha256=_igztEBQKSM4CZro2lvlK0m01eIksqbmyux4w8gY1ks,4095
|
|
10
|
-
plain_auth-0.19.0.dist-info/METADATA,sha256=AgXQNgwVcrO3qQ_-c_fTK94g44PE3uiDgUeVGF6OEAI,4358
|
|
11
|
-
plain_auth-0.19.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
12
|
-
plain_auth-0.19.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
|
|
13
|
-
plain_auth-0.19.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|