plain.auth 0.16.0__tar.gz → 0.18.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.
@@ -11,8 +11,6 @@ plain*/tests/.plain
11
11
  # Ottobot
12
12
  .aider*
13
13
 
14
- /llms-full.txt
15
-
16
14
  # Plain temp dirs
17
15
  .plain
18
16
 
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain.auth
3
- Version: 0.16.0
3
+ Version: 0.18.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-File: LICENSE
7
- Requires-Python: >=3.11
7
+ Requires-Python: >=3.13
8
8
  Requires-Dist: plain-models<1.0.0
9
9
  Requires-Dist: plain-sessions<1.0.0
10
10
  Requires-Dist: plain<1.0.0
@@ -1,5 +1,26 @@
1
1
  # plain-auth changelog
2
2
 
3
+ ## [0.18.0](https://github.com/dropseed/plain/releases/plain-auth@0.18.0) (2025-09-19)
4
+
5
+ ### What's changed
6
+
7
+ - Removed deprecated `constant_time_compare` utility function, replaced with Python's built-in `hmac.compare_digest()` for improved security in session management ([55f3f55](https://github.com/dropseed/plain/commit/55f3f5596d))
8
+
9
+ ### Upgrade instructions
10
+
11
+ - No changes required
12
+
13
+ ## [0.17.0](https://github.com/dropseed/plain/releases/plain-auth@0.17.0) (2025-09-12)
14
+
15
+ ### What's changed
16
+
17
+ - Model managers are now accessed via `.query` instead of `.objects` ([037a239](https://github.com/dropseed/plain/commit/037a239ef4))
18
+ - Updated to require Python 3.13 minimum ([d86e307](https://github.com/dropseed/plain/commit/d86e307efb))
19
+
20
+ ### Upgrade instructions
21
+
22
+ - Replace any usage of `Model.objects` with `Model.query` in your code (e.g., `User.objects.get()` becomes `User.query.get()`)
23
+
3
24
  ## [0.16.0](https://github.com/dropseed/plain/releases/plain-auth@0.16.0) (2025-08-19)
4
25
 
5
26
  ### What's changed
@@ -1,7 +1,10 @@
1
+ import hmac
2
+
1
3
  from plain.exceptions import ImproperlyConfigured
2
4
  from plain.models import models_registry
3
5
  from plain.runtime import settings
4
- from plain.utils.crypto import constant_time_compare, salted_hmac
6
+ from plain.utils.crypto import salted_hmac
7
+ from plain.utils.encoding import force_bytes
5
8
 
6
9
  USER_ID_SESSION_KEY = "_auth_user_id"
7
10
  USER_HASH_SESSION_KEY = "_auth_user_hash"
@@ -60,8 +63,9 @@ def login(request, user):
60
63
  # session if the existing session corresponds to a different
61
64
  # authenticated user.
62
65
  request.session.flush()
63
- elif session_auth_hash and not constant_time_compare(
64
- request.session.get(USER_HASH_SESSION_KEY, ""), session_auth_hash
66
+ elif session_auth_hash and not hmac.compare_digest(
67
+ force_bytes(request.session.get(USER_HASH_SESSION_KEY, "")),
68
+ force_bytes(session_auth_hash),
65
69
  ):
66
70
  # If the session hash does not match the current hash, reset the
67
71
  # session. Most likely this means the password was changed.
@@ -115,7 +119,7 @@ def get_user(request):
115
119
 
116
120
  UserModel = get_user_model()
117
121
  try:
118
- user = UserModel._default_manager.get(id=request.session[USER_ID_SESSION_KEY])
122
+ user = UserModel.query.get(id=request.session[USER_ID_SESSION_KEY])
119
123
  except UserModel.DoesNotExist:
120
124
  return None
121
125
 
@@ -131,15 +135,17 @@ def get_user(request):
131
135
  session_hash_verified = False
132
136
  else:
133
137
  session_auth_hash = get_session_auth_hash(user)
134
- session_hash_verified = constant_time_compare(
135
- session_hash, session_auth_hash
138
+ session_hash_verified = hmac.compare_digest(
139
+ force_bytes(session_hash), force_bytes(session_auth_hash)
136
140
  )
137
141
  if not session_hash_verified:
138
142
  # If the current secret does not verify the session, try
139
143
  # with the fallback secrets and stop when a matching one is
140
144
  # found.
141
145
  if session_hash and any(
142
- constant_time_compare(session_hash, fallback_auth_hash)
146
+ hmac.compare_digest(
147
+ force_bytes(session_hash), force_bytes(fallback_auth_hash)
148
+ )
143
149
  for fallback_auth_hash in get_session_auth_fallback_hash(user)
144
150
  ):
145
151
  request.session.cycle_key()
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "plain.auth"
3
- version = "0.16.0"
3
+ version = "0.18.0"
4
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
- requires-python = ">=3.11"
7
+ requires-python = ">=3.13"
8
8
  dependencies = [
9
9
  "plain<1.0.0",
10
10
  "plain.models<1.0.0",
@@ -22,7 +22,7 @@ def test_admin_required(db):
22
22
  # login required first
23
23
  assert client.get("/admin/").status_code == 302
24
24
 
25
- user = get_user_model().objects.create(username="user")
25
+ user = get_user_model().query.create(username="user")
26
26
  client.force_login(user)
27
27
  # not admin -> 404
28
28
  assert client.get("/admin/").status_code == 404
File without changes
File without changes