sanic-security 1.13.4__py3-none-any.whl → 1.14.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.
- sanic_security/authentication.py +14 -13
- sanic_security/authorization.py +4 -6
- sanic_security/test/server.py +10 -14
- sanic_security/verification.py +4 -2
- {sanic_security-1.13.4.dist-info → sanic_security-1.14.0.dist-info}/METADATA +7 -39
- sanic_security-1.14.0.dist-info/RECORD +16 -0
- sanic_security-1.13.4.dist-info/RECORD +0 -16
- {sanic_security-1.13.4.dist-info → sanic_security-1.14.0.dist-info}/LICENSE +0 -0
- {sanic_security-1.13.4.dist-info → sanic_security-1.14.0.dist-info}/WHEEL +0 -0
- {sanic_security-1.13.4.dist-info → sanic_security-1.14.0.dist-info}/top_level.txt +0 -0
sanic_security/authentication.py
CHANGED
@@ -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.
|
226
|
+
request.ctx.session = authentication_session
|
227
227
|
return authentication_session
|
228
228
|
|
229
229
|
|
@@ -293,34 +293,33 @@ def initialize_security(app: Sanic, create_root=True) -> None:
|
|
293
293
|
|
294
294
|
@app.on_response
|
295
295
|
async def response_handler_middleware(request, response):
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
authentication_session.encode(response)
|
296
|
+
if hasattr(request.ctx, "session"):
|
297
|
+
secure_headers.set_headers(response)
|
298
|
+
if request.ctx.session.is_refresh:
|
299
|
+
request.ctx.session.encode(response)
|
301
300
|
|
302
301
|
@app.listener("before_server_start")
|
303
302
|
async def audit_configuration(app, loop):
|
304
303
|
if security_config.SECRET == DEFAULT_CONFIG["SECRET"]:
|
305
|
-
warnings.warn("Secret should be changed from default.", AuditWarning)
|
304
|
+
warnings.warn("Secret should be changed from default.", AuditWarning, 2)
|
306
305
|
if not security_config.SESSION_HTTPONLY:
|
307
|
-
warnings.warn("HttpOnly should be enabled.", AuditWarning)
|
306
|
+
warnings.warn("HttpOnly should be enabled.", AuditWarning, 2)
|
308
307
|
if not security_config.SESSION_SECURE:
|
309
|
-
warnings.warn("Secure should be enabled.", AuditWarning)
|
308
|
+
warnings.warn("Secure should be enabled.", AuditWarning, 2)
|
310
309
|
if (
|
311
310
|
not security_config.SESSION_SAMESITE
|
312
311
|
or security_config.SESSION_SAMESITE.lower() == "none"
|
313
312
|
):
|
314
|
-
warnings.warn("SameSite should not be none.", AuditWarning)
|
313
|
+
warnings.warn("SameSite should not be none.", AuditWarning, 2)
|
315
314
|
if not security_config.SESSION_DOMAIN:
|
316
|
-
warnings.warn("Domain should not be none.", AuditWarning)
|
315
|
+
warnings.warn("Domain should not be none.", AuditWarning, 2)
|
317
316
|
if (
|
318
317
|
create_root
|
319
318
|
and security_config.INITIAL_ADMIN_EMAIL
|
320
319
|
== DEFAULT_CONFIG["INITIAL_ADMIN_EMAIL"]
|
321
320
|
):
|
322
321
|
warnings.warn(
|
323
|
-
"Initial admin email should be changed from default.", AuditWarning
|
322
|
+
"Initial admin email should be changed from default.", AuditWarning, 2
|
324
323
|
)
|
325
324
|
if (
|
326
325
|
create_root
|
@@ -328,7 +327,9 @@ def initialize_security(app: Sanic, create_root=True) -> None:
|
|
328
327
|
== DEFAULT_CONFIG["INITIAL_ADMIN_PASSWORD"]
|
329
328
|
):
|
330
329
|
warnings.warn(
|
331
|
-
"Initial admin password should be changed from default.",
|
330
|
+
"Initial admin password should be changed from default.",
|
331
|
+
AuditWarning,
|
332
|
+
2,
|
332
333
|
)
|
333
334
|
|
334
335
|
@app.listener("before_server_start")
|
sanic_security/authorization.py
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
sanic_security/test/server.py
CHANGED
@@ -144,16 +144,15 @@ async def on_logout(request):
|
|
144
144
|
@requires_authentication
|
145
145
|
async def on_authenticate(request):
|
146
146
|
"""Authenticate client session and account."""
|
147
|
-
authentication_session = request.ctx.authentication_session
|
148
147
|
response = json(
|
149
148
|
"Authenticated!",
|
150
149
|
{
|
151
150
|
"bearer": (
|
152
|
-
|
153
|
-
if not
|
151
|
+
request.ctx.session.bearer.json
|
152
|
+
if not request.ctx.session.anonymous
|
154
153
|
else None
|
155
154
|
),
|
156
|
-
"refresh":
|
155
|
+
"refresh": request.ctx.session.is_refresh,
|
157
156
|
},
|
158
157
|
)
|
159
158
|
return response
|
@@ -163,10 +162,9 @@ async def on_authenticate(request):
|
|
163
162
|
@requires_authentication
|
164
163
|
async def on_authentication_expire(request):
|
165
164
|
"""Expire client's session."""
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
return json("Authentication expired!", authentication_session.json)
|
165
|
+
request.ctx.session.expiration_date = datetime.datetime.now(datetime.UTC)
|
166
|
+
await request.ctx.session.save(update_fields=["expiration_date"])
|
167
|
+
return json("Authentication expired!", request.ctx.session.json)
|
170
168
|
|
171
169
|
|
172
170
|
@app.post("api/test/auth/associated")
|
@@ -174,7 +172,7 @@ async def on_authentication_expire(request):
|
|
174
172
|
async def on_get_associated_authentication_sessions(request):
|
175
173
|
"""Retrieves authentication sessions associated with logged in account."""
|
176
174
|
authentication_sessions = await AuthenticationSession.get_associated(
|
177
|
-
request.ctx.
|
175
|
+
request.ctx.session.bearer
|
178
176
|
)
|
179
177
|
return json(
|
180
178
|
"Associated authentication sessions retrieved!",
|
@@ -209,7 +207,7 @@ async def on_captcha_audio(request):
|
|
209
207
|
@requires_captcha
|
210
208
|
async def on_captcha_attempt(request):
|
211
209
|
"""Attempt captcha challenge."""
|
212
|
-
return json("Captcha attempt successful!", request.ctx.
|
210
|
+
return json("Captcha attempt successful!", request.ctx.session.json)
|
213
211
|
|
214
212
|
|
215
213
|
@app.post("api/test/two-step/request")
|
@@ -225,9 +223,7 @@ async def on_request_verification(request):
|
|
225
223
|
@requires_two_step_verification
|
226
224
|
async def on_verification_attempt(request):
|
227
225
|
"""Attempt two-step verification challenge."""
|
228
|
-
return json(
|
229
|
-
"Two step verification attempt successful!", request.ctx.two_step_session.json
|
230
|
-
)
|
226
|
+
return json("Two step verification attempt successful!", request.ctx.session.json)
|
231
227
|
|
232
228
|
|
233
229
|
@app.post("api/test/auth/roles")
|
@@ -248,7 +244,7 @@ async def on_role_assign(request):
|
|
248
244
|
"""Assign authenticated account a role."""
|
249
245
|
await assign_role(
|
250
246
|
request.form.get("name"),
|
251
|
-
request.ctx.
|
247
|
+
request.ctx.session.bearer,
|
252
248
|
request.form.get("permissions"),
|
253
249
|
"Role used for testing.",
|
254
250
|
)
|
sanic_security/verification.py
CHANGED
@@ -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
|
-
|
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
|
-
|
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.
|
3
|
+
Version: 1.14.0
|
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/
|
@@ -309,46 +309,15 @@ async def on_authenticate(request):
|
|
309
309
|
@app.post("api/security/auth")
|
310
310
|
@requires_authentication
|
311
311
|
async def on_authenticate(request):
|
312
|
-
|
313
|
-
response = json(
|
314
|
-
"You have been authenticated.",
|
315
|
-
authentication_session.json,
|
316
|
-
)
|
312
|
+
response = json("You have been authenticated.", request.ctx.session.json)
|
317
313
|
return response
|
318
314
|
```
|
319
315
|
|
320
316
|
## CAPTCHA
|
321
317
|
|
322
|
-
Protects against spam and malicious activities by ensuring that only real humans can complete certain actions
|
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
|
-
```
|
318
|
+
Protects against spam and malicious activities by ensuring that only real humans can complete certain actions like
|
319
|
+
submitting a form or creating an account. A font and voice library for CAPTCHA challenges is included in the repository,
|
320
|
+
or you can download/create your own and specify its path in the configuration.
|
352
321
|
|
353
322
|
* Request CAPTCHA
|
354
323
|
|
@@ -393,7 +362,7 @@ async def on_captcha(request):
|
|
393
362
|
@app.post("api/security/captcha")
|
394
363
|
@requires_captcha
|
395
364
|
async def on_captcha(request):
|
396
|
-
return json("Captcha attempt successful!", request.ctx.
|
365
|
+
return json("Captcha attempt successful!", request.ctx.session.json)
|
397
366
|
```
|
398
367
|
|
399
368
|
## Two-step Verification
|
@@ -455,8 +424,7 @@ async def on_two_step_verification(request):
|
|
455
424
|
@requires_two_step_verification
|
456
425
|
async def on_two_step_verification(request):
|
457
426
|
response = json(
|
458
|
-
"Two-step verification attempt successful!",
|
459
|
-
request.ctx.two_step_session.json,
|
427
|
+
"Two-step verification attempt successful!", request.ctx.session.json
|
460
428
|
)
|
461
429
|
return response
|
462
430
|
```
|
@@ -0,0 +1,16 @@
|
|
1
|
+
sanic_security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
sanic_security/authentication.py,sha256=dq7Yt_1xm9_LSZgMZkyzgcvG46NUQsysEb3s5pgO7BE,13165
|
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=hLvT2mG1VXeV7nE6r1avoSOTa1qMSS6jL0qTizyCdOY,12029
|
11
|
+
sanic_security/test/tests.py,sha256=bW5fHJfsCrg8eBmcSqVMLm0R5XRL1ou-XJJRgz09GOE,21993
|
12
|
+
sanic_security-1.14.0.dist-info/LICENSE,sha256=sXlJs9_mG-dCkPfWsDnuzydJWagS82E2gYtkVH9enHA,1100
|
13
|
+
sanic_security-1.14.0.dist-info/METADATA,sha256=ynu6jYXbs9s7ZQi8l2Z2Ilm93Yu8JxQukx-swbOcQIc,23306
|
14
|
+
sanic_security-1.14.0.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
|
15
|
+
sanic_security-1.14.0.dist-info/top_level.txt,sha256=ZybkhHXSjfzhmv8XeqLvnNmLmv21Z0oPX6Ep4DJN8b0,15
|
16
|
+
sanic_security-1.14.0.dist-info/RECORD,,
|
@@ -1,16 +0,0 @@
|
|
1
|
-
sanic_security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
sanic_security/authentication.py,sha256=6pOiZJmnej0no-at7irkrh8YCupAdJVQOJEgfT2cc5k,13214
|
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.4.dist-info/LICENSE,sha256=sXlJs9_mG-dCkPfWsDnuzydJWagS82E2gYtkVH9enHA,1100
|
13
|
-
sanic_security-1.13.4.dist-info/METADATA,sha256=T_LTsAXGsFWZFSntcTzKoEaoPRpz1JIHwLjcPc0Havc,24248
|
14
|
-
sanic_security-1.13.4.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
|
15
|
-
sanic_security-1.13.4.dist-info/top_level.txt,sha256=ZybkhHXSjfzhmv8XeqLvnNmLmv21Z0oPX6Ep4DJN8b0,15
|
16
|
-
sanic_security-1.13.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|