sanic-security 1.13.3__py3-none-any.whl → 1.13.4__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 +5 -8
- sanic_security/authorization.py +5 -3
- sanic_security/configuration.py +3 -0
- sanic_security/models.py +30 -18
- sanic_security/test/server.py +8 -3
- sanic_security/utils.py +13 -0
- {sanic_security-1.13.3.dist-info → sanic_security-1.13.4.dist-info}/METADATA +56 -24
- sanic_security-1.13.4.dist-info/RECORD +16 -0
- sanic_security-1.13.3.dist-info/RECORD +0 -16
- {sanic_security-1.13.3.dist-info → sanic_security-1.13.4.dist-info}/LICENSE +0 -0
- {sanic_security-1.13.3.dist-info → sanic_security-1.13.4.dist-info}/WHEEL +0 -0
- {sanic_security-1.13.3.dist-info → sanic_security-1.13.4.dist-info}/top_level.txt +0 -0
sanic_security/authentication.py
CHANGED
@@ -2,7 +2,6 @@ import functools
|
|
2
2
|
import re
|
3
3
|
import warnings
|
4
4
|
|
5
|
-
from argon2 import PasswordHasher
|
6
5
|
from argon2.exceptions import VerifyMismatchError
|
7
6
|
from sanic import Sanic
|
8
7
|
from sanic.log import logger
|
@@ -18,7 +17,7 @@ from sanic_security.exceptions import (
|
|
18
17
|
AuditWarning,
|
19
18
|
)
|
20
19
|
from sanic_security.models import Account, AuthenticationSession, Role, TwoStepSession
|
21
|
-
from sanic_security.utils import get_ip
|
20
|
+
from sanic_security.utils import get_ip, password_hasher, secure_headers
|
22
21
|
|
23
22
|
"""
|
24
23
|
Copyright (c) 2020-present Nicholas Aidan Stewart
|
@@ -42,8 +41,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
42
41
|
SOFTWARE.
|
43
42
|
"""
|
44
43
|
|
45
|
-
password_hasher = PasswordHasher()
|
46
|
-
|
47
44
|
|
48
45
|
async def register(
|
49
46
|
request: Request, verified: bool = False, disabled: bool = False
|
@@ -193,8 +190,7 @@ async def fulfill_second_factor(request: Request) -> AuthenticationSession:
|
|
193
190
|
authentication_session.requires_second_factor = False
|
194
191
|
await authentication_session.save(update_fields=["requires_second_factor"])
|
195
192
|
logger.info(
|
196
|
-
f"Client {get_ip(request)} has fulfilled authentication session {authentication_session.id} "
|
197
|
-
"second factor."
|
193
|
+
f"Client {get_ip(request)} has fulfilled authentication session {authentication_session.id} second factor."
|
198
194
|
)
|
199
195
|
return authentication_session
|
200
196
|
|
@@ -288,7 +284,7 @@ def validate_password(password: str) -> str:
|
|
288
284
|
|
289
285
|
def initialize_security(app: Sanic, create_root=True) -> None:
|
290
286
|
"""
|
291
|
-
Audits configuration, creates root administrator account, and attaches
|
287
|
+
Audits configuration, creates root administrator account, and attaches response handler middleware.
|
292
288
|
|
293
289
|
Args:
|
294
290
|
app (Sanic): The main Sanic application instance.
|
@@ -296,7 +292,8 @@ def initialize_security(app: Sanic, create_root=True) -> None:
|
|
296
292
|
"""
|
297
293
|
|
298
294
|
@app.on_response
|
299
|
-
async def
|
295
|
+
async def response_handler_middleware(request, response):
|
296
|
+
secure_headers.set_headers(response)
|
300
297
|
if hasattr(request.ctx, "authentication_session"):
|
301
298
|
authentication_session = request.ctx.authentication_session
|
302
299
|
if authentication_session.is_refresh:
|
sanic_security/authorization.py
CHANGED
@@ -71,7 +71,7 @@ async def check_permissions(
|
|
71
71
|
if fnmatch(required_permission, role_permission):
|
72
72
|
return authentication_session
|
73
73
|
logger.warning(
|
74
|
-
f"Client {get_ip(request)} with account {authentication_session.bearer.id} attempted an unauthorized action.
|
74
|
+
f"Client {get_ip(request)} with account {authentication_session.bearer.id} attempted an unauthorized action."
|
75
75
|
)
|
76
76
|
raise AuthorizationError("Insufficient permissions required for this action.")
|
77
77
|
|
@@ -123,14 +123,16 @@ async def assign_role(
|
|
123
123
|
Args:
|
124
124
|
name (str): The name of the role associated with the account.
|
125
125
|
account (Account): The account associated with the created role.
|
126
|
-
permissions (str): The permissions of the role associated with the account. Permissions must be separated via
|
126
|
+
permissions (str): The permissions of the role associated with the account. Permissions must be separated via ", " and in wildcard format.
|
127
127
|
description (str): The description of the role associated with the account.
|
128
128
|
"""
|
129
129
|
try:
|
130
130
|
role = await Role.filter(name=name).get()
|
131
131
|
except DoesNotExist:
|
132
132
|
role = await Role.create(
|
133
|
-
|
133
|
+
name=name,
|
134
|
+
description=description,
|
135
|
+
permissions=permissions,
|
134
136
|
)
|
135
137
|
await account.roles.add(role)
|
136
138
|
return role
|
sanic_security/configuration.py
CHANGED
@@ -36,6 +36,7 @@ DEFAULT_CONFIG = {
|
|
36
36
|
"MAX_CHALLENGE_ATTEMPTS": 5,
|
37
37
|
"CAPTCHA_SESSION_EXPIRATION": 60,
|
38
38
|
"CAPTCHA_FONT": "captcha-font.ttf",
|
39
|
+
"CAPTCHA_VOICE": "captcha-voice/",
|
39
40
|
"TWO_STEP_SESSION_EXPIRATION": 300,
|
40
41
|
"AUTHENTICATION_SESSION_EXPIRATION": 86400,
|
41
42
|
"AUTHENTICATION_REFRESH_EXPIRATION": 604800,
|
@@ -62,6 +63,7 @@ class Config(dict):
|
|
62
63
|
MAX_CHALLENGE_ATTEMPTS (str): The maximum amount of session challenge attempts allowed.
|
63
64
|
CAPTCHA_SESSION_EXPIRATION (int): The amount of seconds till captcha session expiration on creation. Setting to 0 will disable expiration.
|
64
65
|
CAPTCHA_FONT (str): The file path to the font being used for captcha generation.
|
66
|
+
CAPTCHA_VOICE (str): The directory of the voice library being used for audio captcha generation.
|
65
67
|
TWO_STEP_SESSION_EXPIRATION (int): The amount of seconds till two-step session expiration on creation. Setting to 0 will disable expiration.
|
66
68
|
AUTHENTICATION_SESSION_EXPIRATION (int): The amount of seconds till authentication session expiration on creation. Setting to 0 will disable expiration.
|
67
69
|
AUTHENTICATION_REFRESH_EXPIRATION (int): The amount of seconds till authentication session refresh expiration. Setting to 0 will disable refresh mechanism.
|
@@ -82,6 +84,7 @@ class Config(dict):
|
|
82
84
|
MAX_CHALLENGE_ATTEMPTS: int
|
83
85
|
CAPTCHA_SESSION_EXPIRATION: int
|
84
86
|
CAPTCHA_FONT: str
|
87
|
+
CAPTCHA_VOICE: str
|
85
88
|
TWO_STEP_SESSION_EXPIRATION: int
|
86
89
|
AUTHENTICATION_SESSION_EXPIRATION: int
|
87
90
|
AUTHENTICATION_REFRESH_EXPIRATION: int
|
sanic_security/models.py
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
import base64
|
2
2
|
import datetime
|
3
3
|
import re
|
4
|
-
from io import BytesIO
|
5
4
|
from typing import Union
|
6
5
|
|
7
6
|
import jwt
|
8
|
-
from captcha.image import ImageCaptcha
|
9
7
|
from jwt import DecodeError
|
10
8
|
from sanic.request import Request
|
11
9
|
from sanic.response import HTTPResponse, raw
|
@@ -15,7 +13,13 @@ from tortoise.validators import RegexValidator
|
|
15
13
|
|
16
14
|
from sanic_security.configuration import config as security_config
|
17
15
|
from sanic_security.exceptions import *
|
18
|
-
from sanic_security.utils import
|
16
|
+
from sanic_security.utils import (
|
17
|
+
get_ip,
|
18
|
+
get_code,
|
19
|
+
get_expiration_date,
|
20
|
+
image_generator,
|
21
|
+
audio_generator,
|
22
|
+
)
|
19
23
|
|
20
24
|
"""
|
21
25
|
Copyright (c) 2020-present Nicholas Aidan Stewart
|
@@ -68,7 +72,7 @@ class BaseModel(Model):
|
|
68
72
|
@property
|
69
73
|
def json(self) -> dict:
|
70
74
|
"""
|
71
|
-
A JSON serializable dict to be used in
|
75
|
+
A JSON serializable dict to be used in an HTTP request or response.
|
72
76
|
|
73
77
|
Example:
|
74
78
|
Below is an example of this method returning a dict to be used for JSON serialization.
|
@@ -119,7 +123,7 @@ class Account(BaseModel):
|
|
119
123
|
)
|
120
124
|
phone: str = fields.CharField(
|
121
125
|
unique=True,
|
122
|
-
max_length=
|
126
|
+
max_length=15,
|
123
127
|
null=True,
|
124
128
|
validators=[
|
125
129
|
RegexValidator(r"^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$", re.I)
|
@@ -189,9 +193,8 @@ class Account(BaseModel):
|
|
189
193
|
NotFoundError
|
190
194
|
"""
|
191
195
|
try:
|
192
|
-
|
193
|
-
|
194
|
-
except DoesNotExist:
|
196
|
+
return await Account.filter(email=email, deleted=False).get()
|
197
|
+
except (DoesNotExist, ValidationError):
|
195
198
|
raise NotFoundError("Account with this email does not exist.")
|
196
199
|
|
197
200
|
@staticmethod
|
@@ -209,8 +212,7 @@ class Account(BaseModel):
|
|
209
212
|
NotFoundError
|
210
213
|
"""
|
211
214
|
try:
|
212
|
-
|
213
|
-
return account
|
215
|
+
return await Account.filter(username=username, deleted=False).get()
|
214
216
|
except (DoesNotExist, ValidationError):
|
215
217
|
raise NotFoundError("Account with this username does not exist.")
|
216
218
|
|
@@ -230,7 +232,7 @@ class Account(BaseModel):
|
|
230
232
|
"""
|
231
233
|
try:
|
232
234
|
account = await Account.get_via_email(credential)
|
233
|
-
except
|
235
|
+
except NotFoundError as e:
|
234
236
|
if security_config.ALLOW_LOGIN_WITH_USERNAME:
|
235
237
|
account = await Account.get_via_username(credential)
|
236
238
|
else:
|
@@ -281,9 +283,8 @@ class Account(BaseModel):
|
|
281
283
|
NotFoundError
|
282
284
|
"""
|
283
285
|
try:
|
284
|
-
|
285
|
-
|
286
|
-
except DoesNotExist:
|
286
|
+
return await Account.filter(phone=phone, deleted=False).get()
|
287
|
+
except (DoesNotExist, ValidationError):
|
287
288
|
raise NotFoundError("Account with this phone number does not exist.")
|
288
289
|
|
289
290
|
|
@@ -574,10 +575,21 @@ class CaptchaSession(VerificationSession):
|
|
574
575
|
Returns:
|
575
576
|
captcha_image
|
576
577
|
"""
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
578
|
+
return raw(
|
579
|
+
image_generator.generate(self.code, "jpeg").getvalue(),
|
580
|
+
content_type="image/jpeg",
|
581
|
+
)
|
582
|
+
|
583
|
+
def get_audio(self) -> HTTPResponse:
|
584
|
+
"""
|
585
|
+
Retrieves captcha audio file.
|
586
|
+
|
587
|
+
Returns:
|
588
|
+
captcha_audio
|
589
|
+
"""
|
590
|
+
return raw(
|
591
|
+
bytes(audio_generator.generate(self.code)), content_type="audio/mpeg"
|
592
|
+
)
|
581
593
|
|
582
594
|
class Meta:
|
583
595
|
table = "captcha_session"
|
sanic_security/test/server.py
CHANGED
@@ -195,9 +195,14 @@ async def on_captcha_request(request):
|
|
195
195
|
async def on_captcha_image(request):
|
196
196
|
"""Request captcha image."""
|
197
197
|
captcha_session = await CaptchaSession.decode(request)
|
198
|
-
|
199
|
-
|
200
|
-
|
198
|
+
return captcha_session.get_image()
|
199
|
+
|
200
|
+
|
201
|
+
@app.get("api/test/capt/audio")
|
202
|
+
async def on_captcha_audio(request):
|
203
|
+
"""Request captcha audio."""
|
204
|
+
captcha_session = await CaptchaSession.decode(request)
|
205
|
+
return captcha_session.get_audio()
|
201
206
|
|
202
207
|
|
203
208
|
@app.post("api/test/capt")
|
sanic_security/utils.py
CHANGED
@@ -2,8 +2,14 @@ import datetime
|
|
2
2
|
import random
|
3
3
|
import string
|
4
4
|
|
5
|
+
from argon2 import PasswordHasher
|
6
|
+
from captcha.audio import AudioCaptcha
|
7
|
+
from captcha.image import ImageCaptcha
|
5
8
|
from sanic.request import Request
|
6
9
|
from sanic.response import json as sanic_json, HTTPResponse
|
10
|
+
from secure import Secure
|
11
|
+
|
12
|
+
from sanic_security.configuration import config
|
7
13
|
|
8
14
|
"""
|
9
15
|
Copyright (c) 2020-Present Nicholas Aidan Stewart
|
@@ -27,6 +33,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
33
|
SOFTWARE.
|
28
34
|
"""
|
29
35
|
|
36
|
+
image_generator = ImageCaptcha(
|
37
|
+
190, 90, fonts=config.CAPTCHA_FONT.replace(" ", "").split(",")
|
38
|
+
)
|
39
|
+
audio_generator = AudioCaptcha(voicedir=config.CAPTCHA_VOICE)
|
40
|
+
password_hasher = PasswordHasher()
|
41
|
+
secure_headers = Secure.with_default_headers()
|
42
|
+
|
30
43
|
|
31
44
|
def get_ip(request: Request) -> str:
|
32
45
|
"""
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sanic-security
|
3
|
-
Version: 1.13.
|
3
|
+
Version: 1.13.4
|
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/
|
@@ -20,6 +20,7 @@ Requires-Dist: captcha>=0.4
|
|
20
20
|
Requires-Dist: pillow>=9.5.0
|
21
21
|
Requires-Dist: argon2-cffi>=20.1.0
|
22
22
|
Requires-Dist: sanic>=21.3.0
|
23
|
+
Requires-Dist: secure>=1.0.1
|
23
24
|
Provides-Extra: crypto
|
24
25
|
Requires-Dist: cryptography>=3.3.1; extra == "crypto"
|
25
26
|
Provides-Extra: dev
|
@@ -63,7 +64,7 @@ Requires-Dist: cryptography; extra == "dev"
|
|
63
64
|
* [Configuration](#configuration)
|
64
65
|
* [Usage](#usage)
|
65
66
|
* [Authentication](#authentication)
|
66
|
-
* [
|
67
|
+
* [CAPTCHA](#captcha)
|
67
68
|
* [Two-step Verification](#two-step-verification)
|
68
69
|
* [Authorization](#authorization)
|
69
70
|
* [Testing](#testing)
|
@@ -76,21 +77,22 @@ Requires-Dist: cryptography; extra == "dev"
|
|
76
77
|
<!-- ABOUT THE PROJECT -->
|
77
78
|
## About The Project
|
78
79
|
|
79
|
-
Sanic Security is an authentication, authorization, and verification library designed for use with
|
80
|
+
Sanic Security is an authentication, authorization, and verification library designed for use with the
|
81
|
+
[Sanic](https://github.com/huge-success/sanic) framework.
|
80
82
|
|
81
83
|
* Login, registration, and authentication with refresh mechanisms
|
82
84
|
* Role based authorization with wildcard permissions
|
85
|
+
* Image & audio CAPTCHA
|
83
86
|
* Two-factor authentication
|
84
87
|
* Two-step verification
|
85
|
-
* Logging &
|
86
|
-
* Captcha
|
88
|
+
* Logging & auditing
|
87
89
|
|
88
90
|
Visit [security.na-stewart.com](https://security.na-stewart.com) for documentation.
|
89
91
|
|
90
92
|
<!-- GETTING STARTED -->
|
91
93
|
## Getting Started
|
92
94
|
|
93
|
-
In order to get started, please install [
|
95
|
+
In order to get started, please install [PyPI](https://pypi.org/).
|
94
96
|
|
95
97
|
### Installation
|
96
98
|
|
@@ -109,14 +111,9 @@ as an extra requirement.
|
|
109
111
|
pip3 install sanic-security[crypto]
|
110
112
|
````
|
111
113
|
|
112
|
-
* For developers, fork Sanic Security and install development dependencies.
|
113
|
-
```shell
|
114
|
-
pip3 install -e ".[dev]"
|
115
|
-
````
|
116
|
-
|
117
114
|
* Update sanic-security if already installed.
|
118
115
|
```shell
|
119
|
-
pip3 install
|
116
|
+
pip3 install sanic-security --upgrade
|
120
117
|
```
|
121
118
|
|
122
119
|
### Configuration
|
@@ -154,7 +151,8 @@ You can load environment variables with a different prefix via `config.load_envi
|
|
154
151
|
| **SESSION_PREFIX** | tkn | Prefix attached to the beginning of session cookies. |
|
155
152
|
| **MAX_CHALLENGE_ATTEMPTS** | 5 | The maximum amount of session challenge attempts allowed. |
|
156
153
|
| **CAPTCHA_SESSION_EXPIRATION** | 60 | The amount of seconds till captcha session expiration on creation. Setting to 0 will disable expiration. |
|
157
|
-
| **CAPTCHA_FONT** | captcha-font.ttf | The file path to the font being used for captcha generation.
|
154
|
+
| **CAPTCHA_FONT** | captcha-font.ttf | The file path to the font being used for captcha generation. Several fonts can be used by separating them via comma. |
|
155
|
+
| **CAPTCHA_VOICE** | captcha-voice/ | The directory of the voice library being used for audio captcha generation. |
|
158
156
|
| **TWO_STEP_SESSION_EXPIRATION** | 200 | The amount of seconds till two-step session expiration on creation. Setting to 0 will disable expiration. |
|
159
157
|
| **AUTHENTICATION_SESSION_EXPIRATION** | 86400 | The amount of seconds till authentication session expiration on creation. Setting to 0 will disable expiration. |
|
160
158
|
| **AUTHENTICATION_REFRESH_EXPIRATION** | 604800 | The amount of seconds till authentication refresh expiration. Setting to 0 will disable refresh mechanism. |
|
@@ -319,16 +317,40 @@ async def on_authenticate(request):
|
|
319
317
|
return response
|
320
318
|
```
|
321
319
|
|
322
|
-
##
|
320
|
+
## CAPTCHA
|
321
|
+
|
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.
|
323
324
|
|
324
|
-
|
325
|
-
|
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.
|
326
329
|
|
327
330
|
[1001 Free Fonts](https://www.1001fonts.com/)
|
328
331
|
|
329
|
-
|
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.
|
330
336
|
|
331
|
-
|
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
|
+
```
|
352
|
+
|
353
|
+
* Request CAPTCHA
|
332
354
|
|
333
355
|
```python
|
334
356
|
@app.get("api/security/captcha")
|
@@ -339,7 +361,16 @@ async def on_captcha_img_request(request):
|
|
339
361
|
return response
|
340
362
|
```
|
341
363
|
|
342
|
-
*
|
364
|
+
* Request CAPTCHA Audio
|
365
|
+
|
366
|
+
```python
|
367
|
+
@app.get("api/security/captcha/audio")
|
368
|
+
async def on_captcha_audio_request(request):
|
369
|
+
captcha_session = await CaptchaSession.decode(request)
|
370
|
+
return captcha_session.get_audio() # Captcha: LJ0F3U
|
371
|
+
```
|
372
|
+
|
373
|
+
* Attempt CAPTCHA
|
343
374
|
|
344
375
|
| Key | Value |
|
345
376
|
|-------------|--------|
|
@@ -352,7 +383,7 @@ async def on_captcha(request):
|
|
352
383
|
return json("Captcha attempt successful!", captcha_session.json)
|
353
384
|
```
|
354
385
|
|
355
|
-
* Requires
|
386
|
+
* Requires CAPTCHA (This method is not called directly and instead used as a decorator)
|
356
387
|
|
357
388
|
| Key | Value |
|
358
389
|
|-------------|--------|
|
@@ -367,7 +398,7 @@ async def on_captcha(request):
|
|
367
398
|
|
368
399
|
## Two-step Verification
|
369
400
|
|
370
|
-
Two-step verification should be integrated with other custom
|
401
|
+
Two-step verification should be integrated with other custom functionalities, such as account verification during registration.
|
371
402
|
|
372
403
|
* Request Two-step Verification
|
373
404
|
|
@@ -399,7 +430,7 @@ async def on_two_step_resend(request):
|
|
399
430
|
return json("Verification code resend successful!", two_step_session.json)
|
400
431
|
```
|
401
432
|
|
402
|
-
* Two-step Verification
|
433
|
+
* Attempt Two-step Verification
|
403
434
|
|
404
435
|
| Key | Value |
|
405
436
|
|----------|--------|
|
@@ -442,13 +473,14 @@ user's account; this simplifies common operations, such as adding a user, or cha
|
|
442
473
|
|
443
474
|
Wildcard permissions support the concept of multiple levels or parts. For example, you could grant a user the permission
|
444
475
|
`printer:query`, `printer:query,delete`, or `printer:*`.
|
476
|
+
|
445
477
|
* Assign Role
|
446
478
|
|
447
479
|
```python
|
448
480
|
await assign_role(
|
449
481
|
"Chat Room Moderator",
|
450
482
|
account,
|
451
|
-
"channels:view,delete, account:suspend,mute
|
483
|
+
"channels:view,delete, voice:*, account:suspend,mute",
|
452
484
|
"Can read and delete messages in all chat rooms, suspend and mute accounts, and control voice chat.",
|
453
485
|
)
|
454
486
|
```
|
@@ -497,7 +529,7 @@ async def on_check_roles(request):
|
|
497
529
|
|
498
530
|
* Make sure the test Sanic instance (`test/server.py`) is running on your machine.
|
499
531
|
|
500
|
-
* Run the
|
532
|
+
* Run the test client (`test/tests.py`) for results.
|
501
533
|
|
502
534
|
## Tortoise
|
503
535
|
|
@@ -0,0 +1,16 @@
|
|
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,,
|
@@ -1,16 +0,0 @@
|
|
1
|
-
sanic_security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
sanic_security/authentication.py,sha256=Rc2QqAjY17EialuML-wAThIrOxK8FKIFq927bst70IU,13218
|
3
|
-
sanic_security/authorization.py,sha256=1SHx4cU_ibC0o_nEDDYURH_l_K6Q66M0SLzpRQrsIXc,7534
|
4
|
-
sanic_security/configuration.py,sha256=br2hI3MHsTBh3yfPer5f3bkKSWfQdCeqfLqWmaDNVoM,5510
|
5
|
-
sanic_security/exceptions.py,sha256=JiCaBR2kjE1Cj0fc_08y-32fqJJXa_1UCw205T4_RTY,5493
|
6
|
-
sanic_security/models.py,sha256=XF9u3RIU76M19QzNiM4C7LoLHe4uv6tfcELGH4W7L5k,22637
|
7
|
-
sanic_security/utils.py,sha256=XAUNalcTi53qTz0D8xiDyDyRlq7Z7ffNBzUONJZqe90,2705
|
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=79HaNIH1skWTrh2gIbh8WWVNxvYqPA5GlQ8AqRaCsXQ,12094
|
11
|
-
sanic_security/test/tests.py,sha256=bW5fHJfsCrg8eBmcSqVMLm0R5XRL1ou-XJJRgz09GOE,21993
|
12
|
-
sanic_security-1.13.3.dist-info/LICENSE,sha256=sXlJs9_mG-dCkPfWsDnuzydJWagS82E2gYtkVH9enHA,1100
|
13
|
-
sanic_security-1.13.3.dist-info/METADATA,sha256=PUPuAXTXIRtjpdBzYWl9Pz8S3m-VdJB2skIEnHqQdGM,23022
|
14
|
-
sanic_security-1.13.3.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
|
15
|
-
sanic_security-1.13.3.dist-info/top_level.txt,sha256=ZybkhHXSjfzhmv8XeqLvnNmLmv21Z0oPX6Ep4DJN8b0,15
|
16
|
-
sanic_security-1.13.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|