plain.auth 0.14.0__tar.gz → 0.16.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plain_auth-0.16.0/PKG-INFO +183 -0
- plain_auth-0.16.0/plain/auth/CHANGELOG.md +43 -0
- plain_auth-0.16.0/plain/auth/README.md +171 -0
- {plain_auth-0.14.0 → plain_auth-0.16.0}/plain/auth/sessions.py +3 -13
- {plain_auth-0.14.0 → plain_auth-0.16.0}/pyproject.toml +2 -2
- {plain_auth-0.14.0 → plain_auth-0.16.0}/tests/app/users/migrations/0001_initial.py +1 -1
- plain_auth-0.14.0/PKG-INFO +0 -128
- plain_auth-0.14.0/plain/auth/CHANGELOG.md +0 -21
- plain_auth-0.14.0/plain/auth/README.md +0 -116
- {plain_auth-0.14.0 → plain_auth-0.16.0}/.gitignore +0 -0
- {plain_auth-0.14.0 → plain_auth-0.16.0}/LICENSE +0 -0
- {plain_auth-0.14.0 → plain_auth-0.16.0}/README.md +0 -0
- {plain_auth-0.14.0 → plain_auth-0.16.0}/plain/auth/__init__.py +0 -0
- {plain_auth-0.14.0 → plain_auth-0.16.0}/plain/auth/default_settings.py +0 -0
- {plain_auth-0.14.0 → plain_auth-0.16.0}/plain/auth/middleware.py +0 -0
- {plain_auth-0.14.0 → plain_auth-0.16.0}/plain/auth/test.py +0 -0
- {plain_auth-0.14.0 → plain_auth-0.16.0}/plain/auth/utils.py +0 -0
- {plain_auth-0.14.0 → plain_auth-0.16.0}/plain/auth/views.py +0 -0
- {plain_auth-0.14.0 → plain_auth-0.16.0}/tests/app/settings.py +0 -0
- {plain_auth-0.14.0 → plain_auth-0.16.0}/tests/app/urls.py +0 -0
- {plain_auth-0.14.0 → plain_auth-0.16.0}/tests/app/users/migrations/__init__.py +0 -0
- {plain_auth-0.14.0 → plain_auth-0.16.0}/tests/app/users/models.py +0 -0
- {plain_auth-0.14.0 → plain_auth-0.16.0}/tests/test_views.py +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: plain.auth
|
|
3
|
+
Version: 0.16.0
|
|
4
|
+
Summary: Add users to your app and decide what they can access.
|
|
5
|
+
Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: plain-models<1.0.0
|
|
9
|
+
Requires-Dist: plain-sessions<1.0.0
|
|
10
|
+
Requires-Dist: plain<1.0.0
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# plain.auth
|
|
14
|
+
|
|
15
|
+
**Add users to your app and decide what they can access.**
|
|
16
|
+
|
|
17
|
+
- [Overview](#overview)
|
|
18
|
+
- [Authentication setup](#authentication-setup)
|
|
19
|
+
- [Settings configuration](#settings-configuration)
|
|
20
|
+
- [Creating a user model](#creating-a-user-model)
|
|
21
|
+
- [Login views](#login-views)
|
|
22
|
+
- [Checking if a user is logged in](#checking-if-a-user-is-logged-in)
|
|
23
|
+
- [Restricting views](#restricting-views)
|
|
24
|
+
- [Installation](#installation)
|
|
25
|
+
|
|
26
|
+
## Overview
|
|
27
|
+
|
|
28
|
+
The `plain.auth` package provides user authentication and authorization for Plain applications. Here's a basic example of checking if a user is logged in:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
# In a view
|
|
32
|
+
if request.user:
|
|
33
|
+
print(f"Hello, {request.user.email}!")
|
|
34
|
+
else:
|
|
35
|
+
print("You are not logged in.")
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
And restricting a view to logged-in users:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from plain.auth.views import AuthViewMixin
|
|
42
|
+
from plain.views import View
|
|
43
|
+
|
|
44
|
+
class ProfileView(AuthViewMixin, View):
|
|
45
|
+
login_required = True
|
|
46
|
+
|
|
47
|
+
def get(self):
|
|
48
|
+
return f"Welcome, {self.request.user.email}!"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Authentication setup
|
|
52
|
+
|
|
53
|
+
### Settings configuration
|
|
54
|
+
|
|
55
|
+
Configure your authentication settings in `app/settings.py`:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
INSTALLED_PACKAGES = [
|
|
59
|
+
# ...
|
|
60
|
+
"plain.auth",
|
|
61
|
+
"plain.sessions",
|
|
62
|
+
"plain.passwords", # Or another auth method
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
MIDDLEWARE = [
|
|
66
|
+
"plain.sessions.middleware.SessionMiddleware",
|
|
67
|
+
"plain.auth.middleware.AuthenticationMiddleware",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
AUTH_USER_MODEL = "users.User"
|
|
71
|
+
AUTH_LOGIN_URL = "login"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Creating a user model
|
|
75
|
+
|
|
76
|
+
Create your own user model using `plain create users` or manually:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
# app/users/models.py
|
|
80
|
+
from plain import models
|
|
81
|
+
from plain.passwords.models import PasswordField
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class User(models.Model):
|
|
85
|
+
email = models.EmailField()
|
|
86
|
+
password = PasswordField()
|
|
87
|
+
is_admin = models.BooleanField(default=False)
|
|
88
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
89
|
+
|
|
90
|
+
def __str__(self):
|
|
91
|
+
return self.email
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Login views
|
|
95
|
+
|
|
96
|
+
To log users in, you'll need to pair this package with an authentication method:
|
|
97
|
+
|
|
98
|
+
- `plain-passwords` - Username/password authentication
|
|
99
|
+
- `plain-oauth` - OAuth provider authentication
|
|
100
|
+
- `plain-passkeys` (TBD) - WebAuthn/passkey authentication
|
|
101
|
+
- `plain-passlinks` (TBD) - Magic link authentication
|
|
102
|
+
|
|
103
|
+
Example with password authentication:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
# app/urls.py
|
|
107
|
+
from plain.auth.views import LogoutView
|
|
108
|
+
from plain.urls import path
|
|
109
|
+
from plain.passwords.views import PasswordLoginView
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class LoginView(PasswordLoginView):
|
|
113
|
+
template_name = "login.html"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
urlpatterns = [
|
|
117
|
+
path("logout/", LogoutView, name="logout"),
|
|
118
|
+
path("login/", LoginView, name="login"),
|
|
119
|
+
]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Checking if a user is logged in
|
|
123
|
+
|
|
124
|
+
A `request.user` will either be `None` or point to an instance of your `AUTH_USER_MODEL`.
|
|
125
|
+
|
|
126
|
+
In templates:
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
{% if request.user %}
|
|
130
|
+
<p>Hello, {{ request.user.email }}!</p>
|
|
131
|
+
{% else %}
|
|
132
|
+
<p>You are not logged in.</p>
|
|
133
|
+
{% endif %}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
In Python code:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
if request.user:
|
|
140
|
+
print(f"Hello, {request.user.email}!")
|
|
141
|
+
else:
|
|
142
|
+
print("You are not logged in.")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Restricting views
|
|
146
|
+
|
|
147
|
+
Use the [`AuthViewMixin`](./views.py#AuthViewMixin) to restrict views to logged-in users, admin users, or custom logic:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from plain.auth.views import AuthViewMixin
|
|
151
|
+
from plain.exceptions import PermissionDenied
|
|
152
|
+
from plain.views import View
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class LoggedInView(AuthViewMixin, View):
|
|
156
|
+
login_required = True
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class AdminOnlyView(AuthViewMixin, View):
|
|
160
|
+
login_required = True
|
|
161
|
+
admin_required = True
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class CustomPermissionView(AuthViewMixin, View):
|
|
165
|
+
def check_auth(self):
|
|
166
|
+
super().check_auth()
|
|
167
|
+
if not self.request.user.is_special:
|
|
168
|
+
raise PermissionDenied("You're not special!")
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The [`AuthViewMixin`](./views.py#AuthViewMixin) provides:
|
|
172
|
+
|
|
173
|
+
- `login_required` - Requires a logged-in user
|
|
174
|
+
- `admin_required` - Requires `user.is_admin` to be True
|
|
175
|
+
- `check_auth()` - Override for custom authorization logic
|
|
176
|
+
|
|
177
|
+
## Installation
|
|
178
|
+
|
|
179
|
+
Install the `plain.auth` package from [PyPI](https://pypi.org/project/plain.auth/):
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
uv add plain.auth
|
|
183
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# plain-auth changelog
|
|
2
|
+
|
|
3
|
+
## [0.16.0](https://github.com/dropseed/plain/releases/plain-auth@0.16.0) (2025-08-19)
|
|
4
|
+
|
|
5
|
+
### What's changed
|
|
6
|
+
|
|
7
|
+
- Removed automatic CSRF token rotation on login as part of CSRF system refactor using Sec-Fetch-Site headers ([9551508](https://github.com/dropseed/plain/commit/955150800c))
|
|
8
|
+
- Updated README with improved documentation, examples, and better package description ([4ebecd1](https://github.com/dropseed/plain/commit/4ebecd1856))
|
|
9
|
+
|
|
10
|
+
### Upgrade instructions
|
|
11
|
+
|
|
12
|
+
- No changes required
|
|
13
|
+
|
|
14
|
+
## [0.15.0](https://github.com/dropseed/plain/releases/plain-auth@0.15.0) (2025-07-22)
|
|
15
|
+
|
|
16
|
+
### What's changed
|
|
17
|
+
|
|
18
|
+
- Replaced `pk` field references with `id` field references in session management ([4b8fa6a](https://github.com/dropseed/plain/commit/4b8fa6aef1))
|
|
19
|
+
- Simplified user ID handling in sessions by using direct integer storage instead of field serialization ([4b8fa6a](https://github.com/dropseed/plain/commit/4b8fa6aef1))
|
|
20
|
+
|
|
21
|
+
### Upgrade instructions
|
|
22
|
+
|
|
23
|
+
- No changes required
|
|
24
|
+
|
|
25
|
+
## [0.14.0](https://github.com/dropseed/plain/releases/plain-auth@0.14.0) (2025-07-18)
|
|
26
|
+
|
|
27
|
+
### What's changed
|
|
28
|
+
|
|
29
|
+
- Added OpenTelemetry tracing support with automatic user ID attribute setting in auth middleware ([b0224d0](https://github.com/dropseed/plain/commit/b0224d0418))
|
|
30
|
+
|
|
31
|
+
### Upgrade instructions
|
|
32
|
+
|
|
33
|
+
- No changes required
|
|
34
|
+
|
|
35
|
+
## [0.13.0](https://github.com/dropseed/plain/releases/plain-auth@0.13.0) (2025-06-23)
|
|
36
|
+
|
|
37
|
+
### What's changed
|
|
38
|
+
|
|
39
|
+
- Added `login_client` and `logout_client` helpers to `plain.auth.test` for easily logging users in and out of the Django test client ([eb8a023](https://github.com/dropseed/plain/commit/eb8a023)).
|
|
40
|
+
|
|
41
|
+
### Upgrade instructions
|
|
42
|
+
|
|
43
|
+
- No changes required
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# plain.auth
|
|
2
|
+
|
|
3
|
+
**Add users to your app and decide what they can access.**
|
|
4
|
+
|
|
5
|
+
- [Overview](#overview)
|
|
6
|
+
- [Authentication setup](#authentication-setup)
|
|
7
|
+
- [Settings configuration](#settings-configuration)
|
|
8
|
+
- [Creating a user model](#creating-a-user-model)
|
|
9
|
+
- [Login views](#login-views)
|
|
10
|
+
- [Checking if a user is logged in](#checking-if-a-user-is-logged-in)
|
|
11
|
+
- [Restricting views](#restricting-views)
|
|
12
|
+
- [Installation](#installation)
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
The `plain.auth` package provides user authentication and authorization for Plain applications. Here's a basic example of checking if a user is logged in:
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
# In a view
|
|
20
|
+
if request.user:
|
|
21
|
+
print(f"Hello, {request.user.email}!")
|
|
22
|
+
else:
|
|
23
|
+
print("You are not logged in.")
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
And restricting a view to logged-in users:
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from plain.auth.views import AuthViewMixin
|
|
30
|
+
from plain.views import View
|
|
31
|
+
|
|
32
|
+
class ProfileView(AuthViewMixin, View):
|
|
33
|
+
login_required = True
|
|
34
|
+
|
|
35
|
+
def get(self):
|
|
36
|
+
return f"Welcome, {self.request.user.email}!"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Authentication setup
|
|
40
|
+
|
|
41
|
+
### Settings configuration
|
|
42
|
+
|
|
43
|
+
Configure your authentication settings in `app/settings.py`:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
INSTALLED_PACKAGES = [
|
|
47
|
+
# ...
|
|
48
|
+
"plain.auth",
|
|
49
|
+
"plain.sessions",
|
|
50
|
+
"plain.passwords", # Or another auth method
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
MIDDLEWARE = [
|
|
54
|
+
"plain.sessions.middleware.SessionMiddleware",
|
|
55
|
+
"plain.auth.middleware.AuthenticationMiddleware",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
AUTH_USER_MODEL = "users.User"
|
|
59
|
+
AUTH_LOGIN_URL = "login"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Creating a user model
|
|
63
|
+
|
|
64
|
+
Create your own user model using `plain create users` or manually:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
# app/users/models.py
|
|
68
|
+
from plain import models
|
|
69
|
+
from plain.passwords.models import PasswordField
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class User(models.Model):
|
|
73
|
+
email = models.EmailField()
|
|
74
|
+
password = PasswordField()
|
|
75
|
+
is_admin = models.BooleanField(default=False)
|
|
76
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
77
|
+
|
|
78
|
+
def __str__(self):
|
|
79
|
+
return self.email
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Login views
|
|
83
|
+
|
|
84
|
+
To log users in, you'll need to pair this package with an authentication method:
|
|
85
|
+
|
|
86
|
+
- `plain-passwords` - Username/password authentication
|
|
87
|
+
- `plain-oauth` - OAuth provider authentication
|
|
88
|
+
- `plain-passkeys` (TBD) - WebAuthn/passkey authentication
|
|
89
|
+
- `plain-passlinks` (TBD) - Magic link authentication
|
|
90
|
+
|
|
91
|
+
Example with password authentication:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
# app/urls.py
|
|
95
|
+
from plain.auth.views import LogoutView
|
|
96
|
+
from plain.urls import path
|
|
97
|
+
from plain.passwords.views import PasswordLoginView
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class LoginView(PasswordLoginView):
|
|
101
|
+
template_name = "login.html"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
urlpatterns = [
|
|
105
|
+
path("logout/", LogoutView, name="logout"),
|
|
106
|
+
path("login/", LoginView, name="login"),
|
|
107
|
+
]
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Checking if a user is logged in
|
|
111
|
+
|
|
112
|
+
A `request.user` will either be `None` or point to an instance of your `AUTH_USER_MODEL`.
|
|
113
|
+
|
|
114
|
+
In templates:
|
|
115
|
+
|
|
116
|
+
```html
|
|
117
|
+
{% if request.user %}
|
|
118
|
+
<p>Hello, {{ request.user.email }}!</p>
|
|
119
|
+
{% else %}
|
|
120
|
+
<p>You are not logged in.</p>
|
|
121
|
+
{% endif %}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
In Python code:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
if request.user:
|
|
128
|
+
print(f"Hello, {request.user.email}!")
|
|
129
|
+
else:
|
|
130
|
+
print("You are not logged in.")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Restricting views
|
|
134
|
+
|
|
135
|
+
Use the [`AuthViewMixin`](./views.py#AuthViewMixin) to restrict views to logged-in users, admin users, or custom logic:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from plain.auth.views import AuthViewMixin
|
|
139
|
+
from plain.exceptions import PermissionDenied
|
|
140
|
+
from plain.views import View
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class LoggedInView(AuthViewMixin, View):
|
|
144
|
+
login_required = True
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class AdminOnlyView(AuthViewMixin, View):
|
|
148
|
+
login_required = True
|
|
149
|
+
admin_required = True
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class CustomPermissionView(AuthViewMixin, View):
|
|
153
|
+
def check_auth(self):
|
|
154
|
+
super().check_auth()
|
|
155
|
+
if not self.request.user.is_special:
|
|
156
|
+
raise PermissionDenied("You're not special!")
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The [`AuthViewMixin`](./views.py#AuthViewMixin) provides:
|
|
160
|
+
|
|
161
|
+
- `login_required` - Requires a logged-in user
|
|
162
|
+
- `admin_required` - Requires `user.is_admin` to be True
|
|
163
|
+
- `check_auth()` - Override for custom authorization logic
|
|
164
|
+
|
|
165
|
+
## Installation
|
|
166
|
+
|
|
167
|
+
Install the `plain.auth` package from [PyPI](https://pypi.org/project/plain.auth/):
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
uv add plain.auth
|
|
171
|
+
```
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from plain.csrf.middleware import rotate_token
|
|
2
1
|
from plain.exceptions import ImproperlyConfigured
|
|
3
2
|
from plain.models import models_registry
|
|
4
3
|
from plain.runtime import settings
|
|
@@ -8,12 +7,6 @@ USER_ID_SESSION_KEY = "_auth_user_id"
|
|
|
8
7
|
USER_HASH_SESSION_KEY = "_auth_user_hash"
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
def _get_user_id_from_session(request):
|
|
12
|
-
# This value in the session is always serialized to a string, so we need
|
|
13
|
-
# to convert it back to Python whenever we access it.
|
|
14
|
-
return get_user_model()._meta.pk.to_python(request.session[USER_ID_SESSION_KEY])
|
|
15
|
-
|
|
16
|
-
|
|
17
10
|
def get_session_auth_hash(user):
|
|
18
11
|
"""
|
|
19
12
|
Return an HMAC of the password field.
|
|
@@ -62,7 +55,7 @@ def login(request, user):
|
|
|
62
55
|
session_auth_hash = ""
|
|
63
56
|
|
|
64
57
|
if USER_ID_SESSION_KEY in request.session:
|
|
65
|
-
if
|
|
58
|
+
if int(request.session[USER_ID_SESSION_KEY]) != user.id:
|
|
66
59
|
# To avoid reusing another user's session, create a new, empty
|
|
67
60
|
# session if the existing session corresponds to a different
|
|
68
61
|
# authenticated user.
|
|
@@ -78,11 +71,10 @@ def login(request, user):
|
|
|
78
71
|
# typically done after user login to prevent session fixation attacks.
|
|
79
72
|
request.session.cycle_key()
|
|
80
73
|
|
|
81
|
-
request.session[USER_ID_SESSION_KEY] = user.
|
|
74
|
+
request.session[USER_ID_SESSION_KEY] = user.id
|
|
82
75
|
request.session[USER_HASH_SESSION_KEY] = session_auth_hash
|
|
83
76
|
if hasattr(request, "user"):
|
|
84
77
|
request.user = user
|
|
85
|
-
rotate_token(request)
|
|
86
78
|
|
|
87
79
|
|
|
88
80
|
def logout(request):
|
|
@@ -121,11 +113,9 @@ def get_user(request):
|
|
|
121
113
|
if USER_ID_SESSION_KEY not in request.session:
|
|
122
114
|
return None
|
|
123
115
|
|
|
124
|
-
user_id = _get_user_id_from_session(request)
|
|
125
|
-
|
|
126
116
|
UserModel = get_user_model()
|
|
127
117
|
try:
|
|
128
|
-
user = UserModel._default_manager.get(
|
|
118
|
+
user = UserModel._default_manager.get(id=request.session[USER_ID_SESSION_KEY])
|
|
129
119
|
except UserModel.DoesNotExist:
|
|
130
120
|
return None
|
|
131
121
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "plain.auth"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "
|
|
3
|
+
version = "0.16.0"
|
|
4
|
+
description = "Add users to your app and decide what they can access."
|
|
5
5
|
authors = [{name = "Dave Gaeddert", email = "dave.gaeddert@dropseed.dev"}]
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
requires-python = ">=3.11"
|
|
@@ -13,7 +13,7 @@ class Migration(migrations.Migration):
|
|
|
13
13
|
migrations.CreateModel(
|
|
14
14
|
name="User",
|
|
15
15
|
fields=[
|
|
16
|
-
("id", models.
|
|
16
|
+
("id", models.PrimaryKeyField()),
|
|
17
17
|
("username", models.CharField(max_length=255)),
|
|
18
18
|
("is_admin", models.BooleanField(default=False)),
|
|
19
19
|
],
|
plain_auth-0.14.0/PKG-INFO
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: plain.auth
|
|
3
|
-
Version: 0.14.0
|
|
4
|
-
Summary: User authentication and authorization for Plain.
|
|
5
|
-
Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
|
|
6
|
-
License-File: LICENSE
|
|
7
|
-
Requires-Python: >=3.11
|
|
8
|
-
Requires-Dist: plain-models<1.0.0
|
|
9
|
-
Requires-Dist: plain-sessions<1.0.0
|
|
10
|
-
Requires-Dist: plain<1.0.0
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
|
|
13
|
-
# plain.auth
|
|
14
|
-
|
|
15
|
-
Add users to your app and define which views they can access.
|
|
16
|
-
|
|
17
|
-
To log a user in, you'll want to pair this package with:
|
|
18
|
-
|
|
19
|
-
- `plain-passwords`
|
|
20
|
-
- `plain-oauth`
|
|
21
|
-
- `plain-passkeys` (TBD)
|
|
22
|
-
- `plain-passlinks` (TBD)
|
|
23
|
-
|
|
24
|
-
## Installation
|
|
25
|
-
|
|
26
|
-
```python
|
|
27
|
-
# app/settings.py
|
|
28
|
-
INSTALLED_PACKAGES = [
|
|
29
|
-
# ...
|
|
30
|
-
"plain.auth",
|
|
31
|
-
"plain.sessions",
|
|
32
|
-
"plain.passwords",
|
|
33
|
-
]
|
|
34
|
-
|
|
35
|
-
MIDDLEWARE = [
|
|
36
|
-
"plain.sessions.middleware.SessionMiddleware",
|
|
37
|
-
"plain.auth.middleware.AuthenticationMiddleware",
|
|
38
|
-
]
|
|
39
|
-
|
|
40
|
-
AUTH_USER_MODEL = "users.User"
|
|
41
|
-
AUTH_LOGIN_URL = "login"
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Create your own user model (`plain create users`).
|
|
45
|
-
|
|
46
|
-
```python
|
|
47
|
-
# app/users/models.py
|
|
48
|
-
from plain import models
|
|
49
|
-
from plain.passwords.models import PasswordField
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
class User(models.Model):
|
|
53
|
-
email = models.EmailField()
|
|
54
|
-
password = PasswordField()
|
|
55
|
-
is_admin = models.BooleanField(default=False)
|
|
56
|
-
created_at = models.DateTimeField(auto_now_add=True)
|
|
57
|
-
|
|
58
|
-
def __str__(self):
|
|
59
|
-
return self.email
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Define your URL/view where users can log in.
|
|
63
|
-
|
|
64
|
-
```python
|
|
65
|
-
# app/urls.py
|
|
66
|
-
from plain.auth.views import LoginView, LogoutView
|
|
67
|
-
from plain.urls import include, path
|
|
68
|
-
from plain.passwords.views import PasswordLoginView
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class LoginView(PasswordLoginView):
|
|
72
|
-
template_name = "login.html"
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
urlpatterns = [
|
|
76
|
-
path("logout/", LogoutView, name="logout"),
|
|
77
|
-
path("login/", LoginView, name="login"),
|
|
78
|
-
]
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## Checking if a user is logged in
|
|
82
|
-
|
|
83
|
-
A `request.user` will either be `None` or point to an instance of a your `AUTH_USER_MODEL`.
|
|
84
|
-
|
|
85
|
-
So in templates you can do:
|
|
86
|
-
|
|
87
|
-
```html
|
|
88
|
-
{% if request.user %}
|
|
89
|
-
<p>Hello, {{ request.user.email }}!</p>
|
|
90
|
-
{% else %}
|
|
91
|
-
<p>You are not logged in.</p>
|
|
92
|
-
{% endif %}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
Or in Python:
|
|
96
|
-
|
|
97
|
-
```python
|
|
98
|
-
if request.user:
|
|
99
|
-
print(f"Hello, {request.user.email}!")
|
|
100
|
-
else:
|
|
101
|
-
print("You are not logged in.")
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## Restricting views
|
|
105
|
-
|
|
106
|
-
Use the `AuthViewMixin` to restrict views to logged in users, admin users, or custom logic.
|
|
107
|
-
|
|
108
|
-
```python
|
|
109
|
-
from plain.auth.views import AuthViewMixin
|
|
110
|
-
from plain.exceptions import PermissionDenied
|
|
111
|
-
from plain.views import View
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
class LoggedInView(AuthViewMixin, View):
|
|
115
|
-
login_required = True
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
class AdminOnlyView(AuthViewMixin, View):
|
|
119
|
-
login_required = True
|
|
120
|
-
admin_required = True
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
class CustomPermissionView(AuthViewMixin, View):
|
|
124
|
-
def check_auth(self):
|
|
125
|
-
super().check_auth()
|
|
126
|
-
if not self.request.user.is_special:
|
|
127
|
-
raise PermissionDenied("You're not special!")
|
|
128
|
-
```
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# plain-auth changelog
|
|
2
|
-
|
|
3
|
-
## [0.14.0](https://github.com/dropseed/plain/releases/plain-auth@0.14.0) (2025-07-18)
|
|
4
|
-
|
|
5
|
-
### What's changed
|
|
6
|
-
|
|
7
|
-
- Added OpenTelemetry tracing support with automatic user ID attribute setting in auth middleware ([b0224d0](https://github.com/dropseed/plain/commit/b0224d0418))
|
|
8
|
-
|
|
9
|
-
### Upgrade instructions
|
|
10
|
-
|
|
11
|
-
- No changes required
|
|
12
|
-
|
|
13
|
-
## [0.13.0](https://github.com/dropseed/plain/releases/plain-auth@0.13.0) (2025-06-23)
|
|
14
|
-
|
|
15
|
-
### What's changed
|
|
16
|
-
|
|
17
|
-
- Added `login_client` and `logout_client` helpers to `plain.auth.test` for easily logging users in and out of the Django test client ([eb8a023](https://github.com/dropseed/plain/commit/eb8a023)).
|
|
18
|
-
|
|
19
|
-
### Upgrade instructions
|
|
20
|
-
|
|
21
|
-
- No changes required
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
# plain.auth
|
|
2
|
-
|
|
3
|
-
Add users to your app and define which views they can access.
|
|
4
|
-
|
|
5
|
-
To log a user in, you'll want to pair this package with:
|
|
6
|
-
|
|
7
|
-
- `plain-passwords`
|
|
8
|
-
- `plain-oauth`
|
|
9
|
-
- `plain-passkeys` (TBD)
|
|
10
|
-
- `plain-passlinks` (TBD)
|
|
11
|
-
|
|
12
|
-
## Installation
|
|
13
|
-
|
|
14
|
-
```python
|
|
15
|
-
# app/settings.py
|
|
16
|
-
INSTALLED_PACKAGES = [
|
|
17
|
-
# ...
|
|
18
|
-
"plain.auth",
|
|
19
|
-
"plain.sessions",
|
|
20
|
-
"plain.passwords",
|
|
21
|
-
]
|
|
22
|
-
|
|
23
|
-
MIDDLEWARE = [
|
|
24
|
-
"plain.sessions.middleware.SessionMiddleware",
|
|
25
|
-
"plain.auth.middleware.AuthenticationMiddleware",
|
|
26
|
-
]
|
|
27
|
-
|
|
28
|
-
AUTH_USER_MODEL = "users.User"
|
|
29
|
-
AUTH_LOGIN_URL = "login"
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Create your own user model (`plain create users`).
|
|
33
|
-
|
|
34
|
-
```python
|
|
35
|
-
# app/users/models.py
|
|
36
|
-
from plain import models
|
|
37
|
-
from plain.passwords.models import PasswordField
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class User(models.Model):
|
|
41
|
-
email = models.EmailField()
|
|
42
|
-
password = PasswordField()
|
|
43
|
-
is_admin = models.BooleanField(default=False)
|
|
44
|
-
created_at = models.DateTimeField(auto_now_add=True)
|
|
45
|
-
|
|
46
|
-
def __str__(self):
|
|
47
|
-
return self.email
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Define your URL/view where users can log in.
|
|
51
|
-
|
|
52
|
-
```python
|
|
53
|
-
# app/urls.py
|
|
54
|
-
from plain.auth.views import LoginView, LogoutView
|
|
55
|
-
from plain.urls import include, path
|
|
56
|
-
from plain.passwords.views import PasswordLoginView
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
class LoginView(PasswordLoginView):
|
|
60
|
-
template_name = "login.html"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
urlpatterns = [
|
|
64
|
-
path("logout/", LogoutView, name="logout"),
|
|
65
|
-
path("login/", LoginView, name="login"),
|
|
66
|
-
]
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Checking if a user is logged in
|
|
70
|
-
|
|
71
|
-
A `request.user` will either be `None` or point to an instance of a your `AUTH_USER_MODEL`.
|
|
72
|
-
|
|
73
|
-
So in templates you can do:
|
|
74
|
-
|
|
75
|
-
```html
|
|
76
|
-
{% if request.user %}
|
|
77
|
-
<p>Hello, {{ request.user.email }}!</p>
|
|
78
|
-
{% else %}
|
|
79
|
-
<p>You are not logged in.</p>
|
|
80
|
-
{% endif %}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
Or in Python:
|
|
84
|
-
|
|
85
|
-
```python
|
|
86
|
-
if request.user:
|
|
87
|
-
print(f"Hello, {request.user.email}!")
|
|
88
|
-
else:
|
|
89
|
-
print("You are not logged in.")
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
## Restricting views
|
|
93
|
-
|
|
94
|
-
Use the `AuthViewMixin` to restrict views to logged in users, admin users, or custom logic.
|
|
95
|
-
|
|
96
|
-
```python
|
|
97
|
-
from plain.auth.views import AuthViewMixin
|
|
98
|
-
from plain.exceptions import PermissionDenied
|
|
99
|
-
from plain.views import View
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
class LoggedInView(AuthViewMixin, View):
|
|
103
|
-
login_required = True
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
class AdminOnlyView(AuthViewMixin, View):
|
|
107
|
-
login_required = True
|
|
108
|
-
admin_required = True
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
class CustomPermissionView(AuthViewMixin, View):
|
|
112
|
-
def check_auth(self):
|
|
113
|
-
super().check_auth()
|
|
114
|
-
if not self.request.user.is_special:
|
|
115
|
-
raise PermissionDenied("You're not special!")
|
|
116
|
-
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|