sanic-security 1.13.5__py3-none-any.whl → 1.14.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.
@@ -223,7 +223,7 @@ async def authenticate(request: Request) -> AuthenticationSession:
223
223
  authentication_session.bearer.validate()
224
224
  except ExpiredError:
225
225
  authentication_session = await authentication_session.refresh(request)
226
- request.ctx.authentication_session = authentication_session
226
+ request.ctx.session = authentication_session
227
227
  return authentication_session
228
228
 
229
229
 
@@ -291,13 +291,6 @@ def initialize_security(app: Sanic, create_root=True) -> None:
291
291
  create_root (bool): Determines root account creation on initialization.
292
292
  """
293
293
 
294
- @app.on_response
295
- async def response_handler_middleware(request, response):
296
- if hasattr(request.ctx, "authentication_session"):
297
- secure_headers.set_headers(response)
298
- if request.ctx.authentication_session.is_refresh:
299
- request.ctx.authentication_session.encode(response)
300
-
301
294
  @app.listener("before_server_start")
302
295
  async def audit_configuration(app, loop):
303
296
  if security_config.SECRET == DEFAULT_CONFIG["SECRET"]:
@@ -361,3 +354,13 @@ def initialize_security(app: Sanic, create_root=True) -> None:
361
354
  )
362
355
  await account.roles.add(role)
363
356
  logger.info("Initial admin account created.")
357
+
358
+ @app.on_response
359
+ async def response_handler_middleware(request, response):
360
+ if hasattr(request.ctx, "session"):
361
+ secure_headers.set_headers(response)
362
+ if (
363
+ hasattr(request.ctx.session, "is_refresh")
364
+ and request.ctx.session.is_refresh
365
+ ):
366
+ request.ctx.session.encode(response)
@@ -69,6 +69,7 @@ async def check_permissions(
69
69
  required_permissions, role.permissions.split(", ")
70
70
  ):
71
71
  if fnmatch(required_permission, role_permission):
72
+ request.ctx.session = authentication_session
72
73
  return authentication_session
73
74
  logger.warning(
74
75
  f"Client {get_ip(request)} with account {authentication_session.bearer.id} attempted an unauthorized action."
@@ -107,6 +108,7 @@ async def check_roles(request: Request, *required_roles: str) -> AuthenticationS
107
108
  roles = await authentication_session.bearer.roles.filter(deleted=False).all()
108
109
  for role in roles:
109
110
  if role.name in required_roles:
111
+ request.ctx.session = authentication_session
110
112
  return authentication_session
111
113
  logger.warning(
112
114
  f"Client {get_ip(request)} with account {authentication_session.bearer.id} attempted an unauthorized action. "
@@ -168,9 +170,7 @@ def require_permissions(*required_permissions: str):
168
170
  def decorator(func):
169
171
  @functools.wraps(func)
170
172
  async def wrapper(request, *args, **kwargs):
171
- request.ctx.authentication_session = await check_permissions(
172
- request, *required_permissions
173
- )
173
+ await check_permissions(request, *required_permissions)
174
174
  return await func(request, *args, **kwargs)
175
175
 
176
176
  return wrapper
@@ -208,9 +208,7 @@ def require_roles(*required_roles: str):
208
208
  def decorator(func):
209
209
  @functools.wraps(func)
210
210
  async def wrapper(request, *args, **kwargs):
211
- request.ctx.authentication_session = await check_roles(
212
- request, *required_roles
213
- )
211
+ await check_roles(request, *required_roles)
214
212
  return await func(request, *args, **kwargs)
215
213
 
216
214
  return wrapper
@@ -128,7 +128,6 @@ async def on_two_factor_authentication(request):
128
128
  "Authentication session second-factor fulfilled! You are now authenticated.",
129
129
  authentication_session.bearer.json,
130
130
  )
131
- authentication_session.encode(response)
132
131
  return response
133
132
 
134
133
 
@@ -144,16 +143,15 @@ async def on_logout(request):
144
143
  @requires_authentication
145
144
  async def on_authenticate(request):
146
145
  """Authenticate client session and account."""
147
- authentication_session = request.ctx.authentication_session
148
146
  response = json(
149
147
  "Authenticated!",
150
148
  {
151
149
  "bearer": (
152
- authentication_session.bearer.json
153
- if not authentication_session.anonymous
150
+ request.ctx.session.bearer.json
151
+ if not request.ctx.session.anonymous
154
152
  else None
155
153
  ),
156
- "refresh": authentication_session.is_refresh,
154
+ "refresh": request.ctx.session.is_refresh,
157
155
  },
158
156
  )
159
157
  return response
@@ -163,10 +161,9 @@ async def on_authenticate(request):
163
161
  @requires_authentication
164
162
  async def on_authentication_expire(request):
165
163
  """Expire client's session."""
166
- authentication_session = request.ctx.authentication_session
167
- authentication_session.expiration_date = datetime.datetime.now(datetime.UTC)
168
- await authentication_session.save(update_fields=["expiration_date"])
169
- return json("Authentication expired!", authentication_session.json)
164
+ request.ctx.session.expiration_date = datetime.datetime.now(datetime.UTC)
165
+ await request.ctx.session.save(update_fields=["expiration_date"])
166
+ return json("Authentication expired!", request.ctx.session.json)
170
167
 
171
168
 
172
169
  @app.post("api/test/auth/associated")
@@ -174,7 +171,7 @@ async def on_authentication_expire(request):
174
171
  async def on_get_associated_authentication_sessions(request):
175
172
  """Retrieves authentication sessions associated with logged in account."""
176
173
  authentication_sessions = await AuthenticationSession.get_associated(
177
- request.ctx.authentication_session.bearer
174
+ request.ctx.session.bearer
178
175
  )
179
176
  return json(
180
177
  "Associated authentication sessions retrieved!",
@@ -209,7 +206,7 @@ async def on_captcha_audio(request):
209
206
  @requires_captcha
210
207
  async def on_captcha_attempt(request):
211
208
  """Attempt captcha challenge."""
212
- return json("Captcha attempt successful!", request.ctx.captcha_session.json)
209
+ return json("Captcha attempt successful!", request.ctx.session.json)
213
210
 
214
211
 
215
212
  @app.post("api/test/two-step/request")
@@ -225,9 +222,7 @@ async def on_request_verification(request):
225
222
  @requires_two_step_verification
226
223
  async def on_verification_attempt(request):
227
224
  """Attempt two-step verification challenge."""
228
- return json(
229
- "Two step verification attempt successful!", request.ctx.two_step_session.json
230
- )
225
+ return json("Two step verification attempt successful!", request.ctx.session.json)
231
226
 
232
227
 
233
228
  @app.post("api/test/auth/roles")
@@ -248,7 +243,7 @@ async def on_role_assign(request):
248
243
  """Assign authenticated account a role."""
249
244
  await assign_role(
250
245
  request.form.get("name"),
251
- request.ctx.authentication_session.bearer,
246
+ request.ctx.session.bearer,
252
247
  request.form.get("permissions"),
253
248
  "Role used for testing.",
254
249
  )
@@ -102,6 +102,7 @@ async def two_step_verification(request: Request) -> TwoStepSession:
102
102
  logger.info(
103
103
  f"Client {get_ip(request)} has completed two-step session {two_step_session.id} challenge."
104
104
  )
105
+ request.ctx.session = two_step_session
105
106
  return two_step_session
106
107
 
107
108
 
@@ -133,7 +134,7 @@ def requires_two_step_verification(arg=None):
133
134
  def decorator(func):
134
135
  @functools.wraps(func)
135
136
  async def wrapper(request, *args, **kwargs):
136
- request.ctx.two_step_session = await two_step_verification(request)
137
+ await two_step_verification(request)
137
138
  return await func(request, *args, **kwargs)
138
139
 
139
140
  return wrapper
@@ -229,6 +230,7 @@ async def captcha(request: Request) -> CaptchaSession:
229
230
  logger.info(
230
231
  f"Client {get_ip(request)} has completed captcha session {captcha_session.id} challenge."
231
232
  )
233
+ request.ctx.session = captcha_session
232
234
  return captcha_session
233
235
 
234
236
 
@@ -257,7 +259,7 @@ def requires_captcha(arg=None):
257
259
  def decorator(func):
258
260
  @functools.wraps(func)
259
261
  async def wrapper(request, *args, **kwargs):
260
- request.ctx.captcha_session = await captcha(request)
262
+ await captcha(request)
261
263
  return await func(request, *args, **kwargs)
262
264
 
263
265
  return wrapper
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sanic-security
3
- Version: 1.13.5
3
+ Version: 1.14.1
4
4
  Summary: An async security library for the Sanic framework.
5
5
  Author-email: Aidan Stewart <me@na-stewart.com>
6
6
  Project-URL: Documentation, https://security.na-stewart.com/
@@ -21,14 +21,14 @@ Requires-Dist: pillow>=9.5.0
21
21
  Requires-Dist: argon2-cffi>=20.1.0
22
22
  Requires-Dist: sanic>=21.3.0
23
23
  Requires-Dist: secure>=1.0.1
24
- Provides-Extra: crypto
25
- Requires-Dist: cryptography>=3.3.1; extra == "crypto"
26
24
  Provides-Extra: dev
27
25
  Requires-Dist: httpx; extra == "dev"
28
26
  Requires-Dist: black; extra == "dev"
29
27
  Requires-Dist: blacken-docs; extra == "dev"
30
28
  Requires-Dist: pdoc3; extra == "dev"
31
29
  Requires-Dist: cryptography; extra == "dev"
30
+ Provides-Extra: crypto
31
+ Requires-Dist: cryptography>=3.3.1; extra == "crypto"
32
32
 
33
33
  <!-- PROJECT SHIELDS -->
34
34
  <!--
@@ -212,7 +212,7 @@ Verifies the client's account via two-step session code.
212
212
  | **code** | 24KF19 |
213
213
 
214
214
  ```python
215
- @app.post("api/security/verify")
215
+ @app.put("api/security/verify")
216
216
  async def on_verify(request):
217
217
  two_step_session = await verify_account(request)
218
218
  return json("You have verified your account and may login!", two_step_session.json)
@@ -255,14 +255,13 @@ Fulfills client authentication session's second factor requirement via two-step
255
255
  | **code** | XGED2U |
256
256
 
257
257
  ```python
258
- @app.post("api/security/fulfill-2fa")
258
+ @app.put("api/security/fulfill-2fa")
259
259
  async def on_two_factor_authentication(request):
260
260
  authentication_session = await fulfill_second_factor(request)
261
261
  response = json(
262
262
  "Authentication session second-factor fulfilled! You are now authenticated.",
263
263
  authentication_session.json,
264
264
  )
265
- authentication_session.encode(response)
266
265
  return response
267
266
  ```
268
267
 
@@ -309,46 +308,15 @@ async def on_authenticate(request):
309
308
  @app.post("api/security/auth")
310
309
  @requires_authentication
311
310
  async def on_authenticate(request):
312
- authentication_session = request.ctx.authentication_session
313
- response = json(
314
- "You have been authenticated.",
315
- authentication_session.json,
316
- )
311
+ response = json("You have been authenticated.", request.ctx.session.json)
317
312
  return response
318
313
  ```
319
314
 
320
315
  ## CAPTCHA
321
316
 
322
- Protects against spam and malicious activities by ensuring that only real humans can complete certain actions, like
323
- submitting a form or creating an account.
324
-
325
- * Fonts
326
-
327
- A font for captcha challenges is included in the repository. You can set a custom font by downloading a .ttf file and
328
- specifying its path in the configuration.
329
-
330
- [1001 Free Fonts](https://www.1001fonts.com/)
331
-
332
- * Voice Library
333
-
334
- A voice library for captcha challenges is included in the repository. You can generate your own using `espeak` and
335
- `ffmpeg`, then specify the library's directory in the configuration.
336
-
337
- ```bash
338
- # Set the language code
339
- export ESLANG=en
340
-
341
- # Create a directory for the specified language code
342
- mkdir "$ESLANG"
343
-
344
- # Loop through each character (A-Z, 0-9) and create a directory for each
345
- for i in {A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,0,1,2,3,4,5,6,7,8,9}; do
346
- mkdir "$ESLANG/$i"
347
- espeak -a 150 -s 100 -p 15 -v "$ESLANG" "$i" -w "$ESLANG/$i/orig_default.wav"
348
- ffmpeg -i "$ESLANG/$i/orig_default.wav" -ar 8000 -ac 1 -acodec pcm_u8 "$ESLANG/$i/default.wav"
349
- rm "$ESLANG/$i/orig_default.wav"
350
- done
351
- ```
317
+ Protects against spam and malicious activities by ensuring that only real humans can complete certain actions like
318
+ submitting a form or creating an account. A font and voice library for CAPTCHA challenges is included in the repository,
319
+ or you can download/create your own and specify its path in the configuration.
352
320
 
353
321
  * Request CAPTCHA
354
322
 
@@ -393,7 +361,7 @@ async def on_captcha(request):
393
361
  @app.post("api/security/captcha")
394
362
  @requires_captcha
395
363
  async def on_captcha(request):
396
- return json("Captcha attempt successful!", request.ctx.captcha_session.json)
364
+ return json("Captcha attempt successful!", request.ctx.session.json)
397
365
  ```
398
366
 
399
367
  ## Two-step Verification
@@ -455,8 +423,7 @@ async def on_two_step_verification(request):
455
423
  @requires_two_step_verification
456
424
  async def on_two_step_verification(request):
457
425
  response = json(
458
- "Two-step verification attempt successful!",
459
- request.ctx.two_step_session.json,
426
+ "Two-step verification attempt successful!", request.ctx.session.json
460
427
  )
461
428
  return response
462
429
  ```
@@ -0,0 +1,16 @@
1
+ sanic_security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ sanic_security/authentication.py,sha256=ksHa4E82kNXNRKGSqeO28xfQWdF2pJDxUaaQy9snKwU,13299
3
+ sanic_security/authorization.py,sha256=QvLsaOWu3te0Y75tqChrEXQP5CR92nsRU7LXiUWy5cw,7541
4
+ sanic_security/configuration.py,sha256=h-Kh4PalJpjbDcZvVHCzxX5l-GnldP3Fr8OlgGCZNHY,5680
5
+ sanic_security/exceptions.py,sha256=JiCaBR2kjE1Cj0fc_08y-32fqJJXa_1UCw205T4_RTY,5493
6
+ sanic_security/models.py,sha256=bK5daR6Iq7V7aqNSzksH6DGrCXMj2e4feNRhlxlFQMg,22722
7
+ sanic_security/utils.py,sha256=tgewsCAkNl_NkobHaDlZNIgVopQPiD8SWb6UC6tBYNs,3151
8
+ sanic_security/verification.py,sha256=olmpP2AwXKILRVRnDf7AMRJuK5Fs_i5ESHXSH94A-Yk,8694
9
+ sanic_security/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ sanic_security/test/server.py,sha256=RjL9Kfvkfqpm5TXWwFQKKa0J4hfTKgwI6U0s_TAKO8w,11984
11
+ sanic_security/test/tests.py,sha256=bW5fHJfsCrg8eBmcSqVMLm0R5XRL1ou-XJJRgz09GOE,21993
12
+ sanic_security-1.14.1.dist-info/LICENSE,sha256=sXlJs9_mG-dCkPfWsDnuzydJWagS82E2gYtkVH9enHA,1100
13
+ sanic_security-1.14.1.dist-info/METADATA,sha256=It9-aEffADsbk5GJGO0SEG2DUd1wTq7FRl64l5oMcdA,23259
14
+ sanic_security-1.14.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
15
+ sanic_security-1.14.1.dist-info/top_level.txt,sha256=ZybkhHXSjfzhmv8XeqLvnNmLmv21Z0oPX6Ep4DJN8b0,15
16
+ sanic_security-1.14.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.5.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,16 +0,0 @@
1
- sanic_security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- sanic_security/authentication.py,sha256=SbPFze7s86xDsKOwoy37nGB8xffK3pSHGnmGUdlnexA,13225
3
- sanic_security/authorization.py,sha256=ddJWqGJbFIqII5pUW5SxI7h4EyVB-EhrbGM7jsQutOI,7559
4
- sanic_security/configuration.py,sha256=h-Kh4PalJpjbDcZvVHCzxX5l-GnldP3Fr8OlgGCZNHY,5680
5
- sanic_security/exceptions.py,sha256=JiCaBR2kjE1Cj0fc_08y-32fqJJXa_1UCw205T4_RTY,5493
6
- sanic_security/models.py,sha256=bK5daR6Iq7V7aqNSzksH6DGrCXMj2e4feNRhlxlFQMg,22722
7
- sanic_security/utils.py,sha256=tgewsCAkNl_NkobHaDlZNIgVopQPiD8SWb6UC6tBYNs,3151
8
- sanic_security/verification.py,sha256=js2PkqJU6o46atslJ76n-_cYoY5iz5fbyiV0OFwoySo,8668
9
- sanic_security/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- sanic_security/test/server.py,sha256=Rh_L12HPCfagvAyqkHziBD1C4WHAKZ9ht4mTCpX2Yik,12240
11
- sanic_security/test/tests.py,sha256=bW5fHJfsCrg8eBmcSqVMLm0R5XRL1ou-XJJRgz09GOE,21993
12
- sanic_security-1.13.5.dist-info/LICENSE,sha256=sXlJs9_mG-dCkPfWsDnuzydJWagS82E2gYtkVH9enHA,1100
13
- sanic_security-1.13.5.dist-info/METADATA,sha256=WBdJdWbBOUphyj-RaEioPjEl8ksycafckhJTF2COCQU,24248
14
- sanic_security-1.13.5.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
15
- sanic_security-1.13.5.dist-info/top_level.txt,sha256=ZybkhHXSjfzhmv8XeqLvnNmLmv21Z0oPX6Ep4DJN8b0,15
16
- sanic_security-1.13.5.dist-info/RECORD,,