plain.auth 0.21.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.
plain/auth/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # plain-auth changelog
2
2
 
3
+ ## [0.23.0](https://github.com/dropseed/plain/releases/plain-auth@0.23.0) (2026-01-13)
4
+
5
+ ### What's changed
6
+
7
+ - HTTP exception classes renamed to include `Error` suffix and status code: `PermissionDenied` → `ForbiddenError403`, `Http404` → `NotFoundError404` ([5a1f020](https://github.com/dropseed/plain/commit/5a1f020f52))
8
+ - Response classes renamed: `ResponseRedirect` → `RedirectResponse` ([fad5bf2](https://github.com/dropseed/plain/commit/fad5bf28b0))
9
+
10
+ ### Upgrade instructions
11
+
12
+ - Replace `PermissionDenied` with `ForbiddenError403` in any custom auth logic (e.g., `raise PermissionDenied("message")` becomes `raise ForbiddenError403("message")`)
13
+ - Replace `Http404` with `NotFoundError404` if used in auth-related code
14
+ - Replace `ResponseRedirect` with `RedirectResponse` if imported from `plain.http`
15
+
16
+ ## [0.22.0](https://github.com/dropseed/plain/releases/plain-auth@0.22.0) (2025-11-24)
17
+
18
+ ### What's changed
19
+
20
+ - Replaced `AuthViewMixin` with `AuthView` class that inherits from `SessionView` for better typing and simpler view inheritance ([569afd6](https://github.com/dropseed/plain/commit/569afd606d))
21
+
22
+ ### Upgrade instructions
23
+
24
+ - Replace `class MyView(AuthViewMixin, View)` with `class MyView(AuthView)` - the new `AuthView` class already inherits from the appropriate base classes
25
+
3
26
  ## [0.21.0](https://github.com/dropseed/plain/releases/plain-auth@0.21.0) (2025-11-12)
4
27
 
5
28
  ### What's changed
plain/auth/README.md CHANGED
@@ -139,7 +139,7 @@ Use the [`AuthViewMixin`](./views.py#AuthViewMixin) to restrict views to logged-
139
139
 
140
140
  ```python
141
141
  from plain.auth.views import AuthViewMixin
142
- from plain.exceptions import PermissionDenied
142
+ from plain.exceptions import ForbiddenError403
143
143
  from plain.views import View
144
144
 
145
145
 
@@ -156,7 +156,7 @@ class CustomPermissionView(AuthViewMixin, View):
156
156
  def check_auth(self):
157
157
  super().check_auth()
158
158
  if not self.user.is_special:
159
- raise PermissionDenied("You're not special!")
159
+ raise ForbiddenError403("You're not special!")
160
160
  ```
161
161
 
162
162
  The [`AuthViewMixin`](./views.py#AuthViewMixin) provides:
plain/auth/views.py CHANGED
@@ -1,18 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from functools import cached_property
4
- from typing import TYPE_CHECKING, Any
4
+ from typing import Any
5
5
  from urllib.parse import urlparse, urlunparse
6
6
 
7
- from plain.exceptions import PermissionDenied
7
+ from plain.exceptions import ForbiddenError403, NotFoundError404
8
8
  from plain.http import (
9
- Http404,
10
9
  QueryDict,
11
- Response,
12
- ResponseRedirect,
10
+ RedirectResponse,
11
+ ResponseBase,
13
12
  )
14
13
  from plain.runtime import settings
15
- from plain.sessions.views import SessionViewMixin
14
+ from plain.sessions.views import SessionView
16
15
  from plain.urls import reverse
17
16
  from plain.utils.cache import patch_cache_control
18
17
  from plain.views import View
@@ -20,9 +19,6 @@ from plain.views import View
20
19
  from .sessions import logout
21
20
  from .utils import resolve_url
22
21
 
23
- if TYPE_CHECKING:
24
- from plain.http import Request
25
-
26
22
  try:
27
23
  from plain.admin.impersonate import get_request_impersonator
28
24
  except ImportError:
@@ -35,13 +31,11 @@ class LoginRequired(Exception):
35
31
  self.redirect_field_name = redirect_field_name
36
32
 
37
33
 
38
- class AuthViewMixin(SessionViewMixin):
34
+ class AuthView(SessionView):
39
35
  login_required = False
40
36
  admin_required = False # Implies login_required
41
37
  login_url = settings.AUTH_LOGIN_URL
42
38
 
43
- request: Request
44
-
45
39
  @cached_property
46
40
  def user(self) -> Any | None:
47
41
  """Get the authenticated user for this request."""
@@ -57,9 +51,9 @@ class AuthViewMixin(SessionViewMixin):
57
51
 
58
52
  def check_auth(self) -> None:
59
53
  """
60
- Raises either LoginRequired or PermissionDenied.
54
+ Raises either LoginRequired or ForbiddenError403.
61
55
  - LoginRequired can specify a login_url and redirect_field_name
62
- - PermissionDenied can specify a message
56
+ - ForbiddenError403 can specify a message
63
57
  """
64
58
  if not self.login_required and not self.admin_required:
65
59
  return None
@@ -75,16 +69,16 @@ class AuthViewMixin(SessionViewMixin):
75
69
  # Impersonators should be able to view admin pages while impersonating.
76
70
  # There's probably never a case where an impersonator isn't admin, but it can be configured.
77
71
  if not impersonator.is_admin:
78
- raise PermissionDenied(
72
+ raise ForbiddenError403(
79
73
  "You do not have permission to access this page."
80
74
  )
81
75
  return
82
76
 
83
77
  if not self.user.is_admin:
84
78
  # Show a 404 so we don't expose admin urls to non-admin users
85
- raise Http404()
79
+ raise NotFoundError404()
86
80
 
87
- def get_response(self) -> Response:
81
+ def get_response(self) -> ResponseBase:
88
82
  try:
89
83
  self.check_auth()
90
84
  except LoginRequired as e:
@@ -108,10 +102,9 @@ class AuthViewMixin(SessionViewMixin):
108
102
  e.redirect_field_name,
109
103
  )
110
104
  else:
111
- raise PermissionDenied("Login required")
105
+ raise ForbiddenError403("Login required")
112
106
 
113
- # Mixin expects to be used with View base class
114
- response = super().get_response() # type: ignore[misc]
107
+ response = super().get_response()
115
108
 
116
109
  if self.user:
117
110
  # Make sure it at least has private as a default
@@ -121,14 +114,14 @@ class AuthViewMixin(SessionViewMixin):
121
114
 
122
115
 
123
116
  class LogoutView(View):
124
- def post(self) -> ResponseRedirect:
117
+ def post(self) -> RedirectResponse:
125
118
  logout(self.request)
126
- return ResponseRedirect("/")
119
+ return RedirectResponse("/")
127
120
 
128
121
 
129
122
  def redirect_to_login(
130
123
  next: str, login_url: str | None = None, redirect_field_name: str = "next"
131
- ) -> ResponseRedirect:
124
+ ) -> RedirectResponse:
132
125
  """
133
126
  Redirect the user to the login page, passing the given 'next' page.
134
127
  """
@@ -140,4 +133,4 @@ def redirect_to_login(
140
133
  querystring[redirect_field_name] = next
141
134
  login_url_parts[4] = querystring.urlencode(safe="/")
142
135
 
143
- return ResponseRedirect(str(urlunparse(login_url_parts)))
136
+ return RedirectResponse(str(urlunparse(login_url_parts)))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain.auth
3
- Version: 0.21.0
3
+ Version: 0.23.0
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-Expression: BSD-3-Clause
@@ -152,7 +152,7 @@ Use the [`AuthViewMixin`](./views.py#AuthViewMixin) to restrict views to logged-
152
152
 
153
153
  ```python
154
154
  from plain.auth.views import AuthViewMixin
155
- from plain.exceptions import PermissionDenied
155
+ from plain.exceptions import ForbiddenError403
156
156
  from plain.views import View
157
157
 
158
158
 
@@ -169,7 +169,7 @@ class CustomPermissionView(AuthViewMixin, View):
169
169
  def check_auth(self):
170
170
  super().check_auth()
171
171
  if not self.user.is_special:
172
- raise PermissionDenied("You're not special!")
172
+ raise ForbiddenError403("You're not special!")
173
173
  ```
174
174
 
175
175
  The [`AuthViewMixin`](./views.py#AuthViewMixin) provides:
@@ -1,5 +1,5 @@
1
- plain/auth/CHANGELOG.md,sha256=MR-I5AFmEmAymJ8juWRhxhDAbMTTdGFnw5MXmX37Smc,7116
2
- plain/auth/README.md,sha256=I1SeOyrBnF0GAjD7T0k5OlZ2bSHz9---nElinaobewc,4030
1
+ plain/auth/CHANGELOG.md,sha256=5Qmh-kOTzUDAuT7ncQs2L6PGsmGh7ToBlwZQE1ZBlUg,8413
2
+ plain/auth/README.md,sha256=397DaiamLW8JHoVwNrrIO8NAgRS9jWOdeAZB_Oq135A,4032
3
3
  plain/auth/__init__.py,sha256=CrOsS74CPGN1nPTTfie13mPgdyVLRyZ1YwDPIA77uaA,179
4
4
  plain/auth/default_settings.py,sha256=65VzDn3j61OMn78Lg6Zuds4A8QKzJJ_0G9KoFqAOIRo,466
5
5
  plain/auth/requests.py,sha256=jUlMTOWFt0qfDF_uVkOqaP9rZiCLrAofsmirCwyRGEE,880
@@ -7,8 +7,8 @@ plain/auth/sessions.py,sha256=xDp1EiB0cV5w3L5XioqO8vs77wnF8cvreExms3-e744,6015
7
7
  plain/auth/templates.py,sha256=CVtuIdBgOgYB2o61zpOPJb6nMx_gU61i_3PDQx8FjVs,770
8
8
  plain/auth/test.py,sha256=SHawhwarJEMVfaLkjpiuFVSWZoGIMwhzXreU_T1zvCE,1599
9
9
  plain/auth/utils.py,sha256=9kKWh1QqxA8Esct-jBvTCdjBYOHpO_Tg1YeV9WxYmxg,1362
10
- plain/auth/views.py,sha256=Y3J9RLF4vsgThtoG4mekBmZzegY9wFCMSBuv9fEag_Y,5100
11
- plain_auth-0.21.0.dist-info/METADATA,sha256=J7f90sEWXinkqkzVdGl9QzP2_wQLBtl7tw5_lu_K1iQ,4423
12
- plain_auth-0.21.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
- plain_auth-0.21.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
14
- plain_auth-0.21.0.dist-info/RECORD,,
10
+ plain/auth/views.py,sha256=zQO0LFioYIv35xXe1RBx77MLIJjog1neuX7aIT0wLhE,4943
11
+ plain_auth-0.23.0.dist-info/METADATA,sha256=8CCcWoKfBjxlFTsykBTgexSgSqDSvoUR8ZnHONQBdwg,4425
12
+ plain_auth-0.23.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
13
+ plain_auth-0.23.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
14
+ plain_auth-0.23.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any