summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCoprDistGit <infra@openeuler.org>2023-05-15 03:24:55 +0000
committerCoprDistGit <infra@openeuler.org>2023-05-15 03:24:55 +0000
commitb6dec8f4028df6a7d8a0a58ab7bc5e608e7046eb (patch)
tree061f87b85169ecf040613db50c81a929c392442f
parentdb41b0328d2c0fff7c37b756876678196bd80762 (diff)
automatic import of python-sanic-security
-rw-r--r--.gitignore1
-rw-r--r--python-sanic-security.spec1734
-rw-r--r--sources1
3 files changed, 1736 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29..e5e06a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/sanic-security-1.10.3.tar.gz
diff --git a/python-sanic-security.spec b/python-sanic-security.spec
new file mode 100644
index 0000000..7567c36
--- /dev/null
+++ b/python-sanic-security.spec
@@ -0,0 +1,1734 @@
+%global _empty_manifest_terminate_build 0
+Name: python-sanic-security
+Version: 1.10.3
+Release: 1
+Summary: An effective, simple, and async security library for the Sanic framework.
+License: GNU Affero General Public License v3.0
+URL: https://github.com/na-stewart/sanic-security
+Source0: https://mirrors.nju.edu.cn/pypi/web/packages/91/ab/7b7c945b7300d9262df8fc5be6a4b09b13476501de6b371395ca8eb4ff2a/sanic-security-1.10.3.tar.gz
+BuildArch: noarch
+
+
+%description
+<!-- PROJECT SHIELDS -->
+<!--
+*** I'm using markdown "reference style" links for readability.
+*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).
+*** See the bottom of this document for the declaration of the reference variables
+*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.
+*** https://www.markdownguide.org/basic-syntax/#reference-style-links
+-->
+
+[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
+[![Downloads](https://pepy.tech/badge/sanic-security)](https://pepy.tech/project/sanic-security)
+[![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/sanic-security.svg)](https://anaconda.org/conda-forge/sanic-security)
+
+
+<!-- PROJECT LOGO -->
+<br />
+<p align="center">
+ <h3 align="center">Sanic Security</h3>
+ <p align="center">
+ An effective, simple, and async security library for the Sanic framework.
+ </p>
+</p>
+
+
+<!-- TABLE OF CONTENTS -->
+## Table of Contents
+
+* [About the Project](#about-the-project)
+* [Getting Started](#getting-started)
+ * [Prerequisites](#prerequisites)
+ * [Installation](#installation)
+ * [Configuration](#configuration)
+* [Usage](#usage)
+ * [Authentication](#authentication)
+ * [Captcha](#captcha)
+ * [Two Step Verification](#two-step-verification)
+ * [Authorization](#authorization)
+ * [Testing](#testing)
+ * [Tortoise](#tortoise)
+* [Contributing](#contributing)
+* [License](#license)
+* [Versioning](#versioning)
+* [Support](https://discord.gg/JHpZkMfKTJ)
+
+<!-- ABOUT THE PROJECT -->
+## About The Project
+
+Sanic Security is an authentication, authorization, and verification library designed for use with [Sanic](https://github.com/huge-success/sanic).
+This library contains a variety of features including:
+
+* Login, registration, and authentication
+* Two-factor authentication
+* Captcha
+* Two-step verification
+* Role based authorization with wildcard permissions
+
+Please visit [security.na-stewart.com](https://security.na-stewart.com) for documentation.
+
+<!-- GETTING STARTED -->
+## Getting Started
+
+In order to get started, please install [Pip](https://pypi.org/).
+
+### Installation
+
+* Install the Sanic Security pip package.
+```shell
+pip3 install sanic-security
+````
+
+* Install the Sanic Security pip package with the `cryptography` dependency included.
+
+If you are planning on encoding or decoding JWTs using certain digital signature algorithms (like RSA or ECDSA which use
+the public secret and private secret), you will need to install the `cryptography` library. This can be installed explicitly, or
+as a required extra in the `sanic-security` requirement.
+
+```shell
+pip3 install sanic-security[crypto]
+````
+
+* For developers, fork Sanic Security and install development dependencies.
+```shell
+pip3 install -e ".[dev]"
+````
+
+### Configuration
+
+Sanic Security configuration is merely an object that can be modified either using dot-notation or like a
+dictionary.
+
+For example:
+
+```python
+from sanic_security.configuration import config
+
+config.SECRET = "This is a big secret. Shhhhh"
+config["CAPTCHA_FONT"] = "./resources/captcha.ttf"
+```
+
+You can also use the update() method like on regular dictionaries.
+
+Any environment variables defined with the SANIC_SECURITY_ prefix will be applied to the config. For example, setting
+SANIC_SECURITY_SECRET will be loaded by the application automatically and fed into the SECRET config variable.
+
+You can load environment variables with a different prefix via calling the `config.load_environment_variables("NEW_PREFIX_")` method.
+
+* Default configuration values:
+
+| Key | Value | Description |
+|---------------------------------------|------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
+| **SECRET** | This is a big secret. Shhhhh | The secret used for generating and signing JWTs. This should be a string unique to your application. Keep it safe. |
+| **PUBLIC_SECRET** | None | The secret used for verifying and decoding JWTs and can be publicly shared. This should be a string unique to your application. |
+| **SESSION_SAMESITE** | strict | The SameSite attribute of session cookies. |
+| **SESSION_SECURE** | True | The Secure attribute of session cookies. |
+| **SESSION_HTTPONLY** | True | The HttpOnly attribute of session cookies. HIGHLY recommended that you do not turn this off, unless you know what you are doing. |
+| **SESSION_DOMAIN** | None | The Domain attribute of session cookies. |
+| **SESSION_ENCODING_ALGORITHM** | HS256 | The algorithm used to encode and decode session JWT's. |
+| **SESSION_PREFIX** | token | Prefix attached to the beginning of session cookies. |
+| **MAX_CHALLENGE_ATTEMPTS** | 5 | The maximum amount of session challenge attempts allowed. |
+| **CAPTCHA_SESSION_EXPIRATION** | 60 | The amount of seconds till captcha session expiration on creation. Setting to 0 will disable expiration. |
+| **CAPTCHA_FONT** | captcha.ttf | The file path to the font being used for captcha generation. |
+| **TWO_STEP_SESSION_EXPIRATION** | 200 | The amount of seconds till two step session expiration on creation. Setting to 0 will disable expiration. |
+| **AUTHENTICATION_SESSION_EXPIRATION** | 2692000 | The amount of seconds till authentication session expiration on creation. Setting to 0 will disable expiration. |
+| **ALLOW_LOGIN_WITH_USERNAME** | False | Allows login via username and email. |
+| **INITIAL_ADMIN_EMAIL** | admin@example.com | Email used when creating the initial admin account. |
+| **INITIAL_ADMIN_PASSWORD** | admin123 | Password used when creating the initial admin account. |
+
+## Usage
+
+Sanic Security's authentication and verification functionality is session based. A new session will be created for the user after the user logs in or requests some form of verification (two-step, captcha). The session data is then encoded into a JWT and stored on a cookie on the user’s browser. The session cookie would be sent
+along with every subsequent request. The server can then compare the session stored on the cookie against the session information stored in the database to verify user’s identity and send a response with the corresponding state.
+
+The tables in the below examples represent example [request form-data](https://sanicframework.org/en/guide/basics/request.html#form).
+
+## Authentication
+
+* Initial Administrator Account
+
+This account can be logged into and has complete authoritative access. Login credentials can be modified in config.
+
+ ```python
+ create_initial_admin_account(app)
+ if __name__ == "__main__":
+ app.run(host="127.0.0.1", port=8000)
+ ```
+
+* Registration (With Two-step Verification)
+
+Phone can be null or empty.
+
+| Key | Value |
+|--------------|---------------------|
+| **username** | example |
+| **email** | example@example.com |
+| **phone** | 19811354186 |
+| **password** | examplepass |
+
+```python
+@app.post("api/security/register")
+async def on_register(request):
+ account = await register(request)
+ two_step_session = await request_two_step_verification(request, account)
+ await email_code(
+ account.email, two_step_session.code # Code = AJ8HGD
+ ) # Custom method for emailing verification code.
+ response = json(
+ "Registration successful! Email verification required.",
+ two_step_session.bearer.json,
+ )
+ two_step_session.encode(response)
+ return response
+```
+
+* Verify Account
+
+Verifies the client's account via two-step session code.
+
+| Key | Value |
+|----------|--------|
+| **code** | AJ8HGD |
+
+```python
+@app.post("api/security/verify")
+async def on_verify(request):
+ two_step_session = await verify_account(request)
+ return json(
+ "You have verified your account and may login!", two_step_session.bearer.json
+ )
+```
+
+* Login (With Two-factor Authentication)
+
+Login credentials are retrieved via the Authorization header. Credentials are constructed by first combining the
+username and the password with a colon (aladdin:opensesame), and then by encoding the resulting string in base64
+(YWxhZGRpbjpvcGVuc2VzYW1l). Here is an example authorization header: `Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l`.
+
+You can use a username as well as an email for login if `ALLOW_LOGIN_WITH_USERNAME` is true in the config.
+
+```python
+@app.post("api/security/login")
+async def on_login(request):
+ authentication_session = await login(request, require_second_factor=True)
+ two_step_session = await request_two_step_verification(
+ request, authentication_session.bearer
+ )
+ await email_code(
+ authentication_session.bearer.email, two_step_session.code # Code = BG5KLP
+ ) # Custom method for emailing verification code.
+ response = json(
+ "Login successful! Two-factor authentication required.",
+ authentication_session.bearer.json,
+ )
+ authentication_session.encode(response)
+ two_step_session.encode(response)
+ return response
+```
+
+* Fulfill Second Factor
+
+Fulfills client authentication session's second factor requirement via two-step session code.
+
+| Key | Value |
+|----------|--------|
+| **code** | BG5KLP |
+
+```python
+@app.post("api/security/validate-2fa")
+async def on_two_factor_authentication(request):
+ authentication_session = await fulfill_second_factor(request)
+ response = json(
+ "Authentication session second-factor fulfilled! You are now authenticated.",
+ authentication_session.bearer.json,
+ )
+ authentication_session.encode(response)
+ return response
+```
+
+* Logout
+
+```python
+@app.post("api/security/logout")
+async def on_logout(request):
+ authentication_session = await logout(request)
+ response = json("Logout successful!", authentication_session.bearer.json)
+ return response
+```
+
+* Authenticate
+
+```python
+@app.post("api/security/auth")
+async def on_authenticate(request):
+ authentication_session = await authenticate(request)
+ return json(
+ "You have been authenticated.",
+ authentication_session.bearer.json,
+ )
+```
+
+* Requires Authentication (This method is not called directly and instead used as a decorator.)
+
+```python
+@app.post("api/security/auth")
+@requires_authentication()
+async def on_authenticate(request, authentication_session):
+ return json(
+ "You have been authenticated.",
+ authentication_session.bearer.json,
+ )
+```
+
+## Captcha
+
+A pre-existing font for captcha challenges is included in the Sanic Security repository. You may set your own font by
+downloading a .ttf font and defining the file's path in the configuration.
+
+[1001 Free Fonts](https://www.1001fonts.com/)
+
+[Recommended Font](https://www.1001fonts.com/source-sans-pro-font.html)
+
+Captcha challenge example:
+
+[![Captcha image.](https://github.com/sunset-developer/sanic-security/blob/main/images/captcha.png)](https://github.com/sunset-developer/sanic-security/blob/main/images/captcha.png)
+
+* Request Captcha
+
+```python
+@app.get("api/security/captcha")
+async def on_captcha_img_request(request):
+ captcha_session = await request_captcha(request)
+ response = captcha_session.get_image() # Captcha: FV9NMQ
+ captcha_session.encode(response)
+ return response
+```
+
+* Captcha
+
+| Key | Value |
+|-------------|--------|
+| **captcha** | FV9NMQ |
+
+```python
+@app.post("api/security/captcha")
+async def on_captcha(request):
+ captcha_session = await captcha(request)
+ return json("Captcha attempt successful!", captcha_session.json)
+```
+
+* Requires Captcha (This method is not called directly and instead used as a decorator.)
+
+| Key | Value |
+|-------------|--------|
+| **captcha** | FV9NMQ |
+
+```python
+@app.post("ap/security/captcha")
+@requires_captcha()
+async def on_captcha(request, captcha_session):
+ return json("Captcha attempt successful!", captcha_session.json)
+```
+
+## Two-step Verification
+
+Two-step verification should be integrated with other custom functionality. For example, account verification during registration.
+
+* Request Two-step Verification
+
+| Key | Value |
+|-------------|---------------------|
+| **email** | example@example.com |
+
+```python
+@app.post("api/security/two-step/request")
+async def on_two_step_request(request):
+ two_step_session = await request_two_step_verification(request)
+ await email_code(
+ account.email, two_step_session.code # Code = DT6JZX
+ ) # Custom method for emailing verification code.
+ response = json("Verification request successful!", two_step_session.bearer.json)
+ two_step_session.encode(response)
+ return response
+```
+
+* Resend Two-step Verification Code
+
+```python
+@app.post("api/security/two-step/resend")
+async def on_two_step_resend(request):
+ two_step_session = await TwoStepSession.decode(request)
+ await email_code(
+ account.email, two_step_session.code # Code = DT6JZX
+ ) # Custom method for emailing verification code.
+ return json("Verification code resend successful!", two_step_session.bearer.json)
+```
+
+* Two-step Verification
+
+| Key | Value |
+|----------|--------|
+| **code** | DT6JZX |
+
+```python
+@app.post("api/security/two-step")
+async def on_two_step_verification(request):
+ two_step_session = await two_step_verification(request)
+ response = json(
+ "Two-step verification attempt successful!", two_step_session.bearer.json
+ )
+ return response
+```
+
+* Requires Two-step Verification (This method is not called directly and instead used as a decorator.)
+
+| Key | Value |
+|----------|--------|
+| **code** | DT6JZX |
+
+```python
+@app.post("api/security/two-step")
+@requires_two_step_verification()
+async def on_two_step_verification(request, two_step_session):
+ response = json(
+ "Two-step verification attempt successful!", two_step_session.bearer.json
+ )
+ return response
+```
+
+## Authorization
+
+Sanic Security uses role based authorization with wildcard permissions.
+
+Roles are created for various job functions. The permissions to perform certain operations are assigned to specific roles.
+Users are assigned particular roles, and through those role assignments acquire the permissions needed to perform
+particular system functions. Since users are not assigned permissions directly, but only acquire them through their
+role (or roles), management of individual user rights becomes a matter of simply assigning appropriate roles to the
+user's account; this simplifies common operations, such as adding a user, or changing a user's department.
+
+Wildcard permissions support the concept of multiple levels or parts. For example, you could grant a user the permission
+`printer:query`, `printer:query,delete`, or `printer:*`.
+* Assign Role
+
+```python
+await assign_role(
+ "Chat Room Moderator",
+ account,
+ "channels:view,delete, account:suspend,mute, voice:*",
+ "Can read and delete messages in all chat rooms, suspend and mute accounts, and control voice chat.",
+)
+```
+
+* Check Permissions
+
+```python
+@app.post("api/security/perms")
+async def on_check_perms(request):
+ authentication_session = await check_permissions(
+ request, "channels:view", "voice:*"
+ )
+ return text("Account is authorized.")
+```
+
+
+* Require Permissions (This method is not called directly and instead used as a decorator.)
+
+```python
+@app.post("api/security/perms")
+@require_permissions("channels:view", "voice:*")
+async def on_check_perms(request, authentication_session):
+ return text("Account is authorized.")
+```
+
+* Check Roles
+
+```python
+@app.post("api/security/roles")
+async def on_check_roles(request):
+ authentication_session = await check_roles(request, "Chat Room Moderator")
+ return text("Account is authorized.")
+```
+
+* Require Roles (This method is not called directly and instead used as a decorator.)
+
+```python
+@app.post("api/security/roles")
+@require_roles("Chat Room Moderator")
+async def on_check_roles(request, authentication_session):
+ return text("Account is authorized.")
+```
+
+## Testing
+
+* Set the `TEST_DATABASE_URL` configuration value.
+
+* Make sure the test Sanic instance (`test/server.py`) is running on your machine.
+
+* Run the unit test client (`test/tests.py`) for results.
+
+## Tortoise
+
+Sanic Security uses [Tortoise ORM](https://tortoise-orm.readthedocs.io/en/latest/index.html) for database operations.
+
+Tortoise ORM is an easy-to-use asyncio ORM (Object Relational Mapper).
+
+* Initialise your models and database like so:
+
+```python
+async def init():
+ await Tortoise.init(
+ db_url="sqlite://db.sqlite3",
+ modules={"models": ["sanic_security.models", "app.models"]},
+ )
+ await Tortoise.generate_schemas()
+```
+
+or
+
+```python
+register_tortoise(
+ app,
+ db_url="sqlite://db.sqlite3",
+ modules={"models": ["sanic_security.models", "app.models"]},
+ generate_schemas=True,
+)
+```
+
+* Define your models like so:
+
+```python
+from tortoise.models import Model
+from tortoise import fields
+
+
+class Tournament(Model):
+ id = fields.IntField(pk=True)
+ name = fields.TextField()
+```
+
+* Use it like so:
+
+```python
+# Create instance by save
+tournament = Tournament(name="New Tournament")
+await tournament.save()
+
+# Or by .create()
+await Tournament.create(name="Another Tournament")
+
+# Now search for a record
+tour = await Tournament.filter(name__contains="Another").first()
+print(tour.name)
+```
+
+<!-- CONTRIBUTING -->
+## Contributing
+
+Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
+
+1. Fork the Project
+2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
+3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
+4. Push to the Branch (`git push origin feature/AmazingFeature`)
+5. Open a Pull Request
+
+
+<!-- LICENSE -->
+## License
+
+Distributed under the GNU Affero General Public License v3.0. See `LICENSE` for more information.
+
+<!-- Versioning -->
+## Versioning
+
+**0.0.0**
+
+* MAJOR version when you make incompatible API changes.
+
+* MINOR version when you add functionality in a backwards compatible manner.
+
+* PATCH version when you make backwards compatible bug fixes.
+
+[https://semver.org/](https://semver.org/)
+
+<!-- MARKDOWN LINKS & IMAGES -->
+<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
+[contributors-shield]: https://img.shields.io/github/contributors/sunset-developer/sanic-security.svg?style=flat-square
+[contributors-url]: https://github.com/sunset-developer/sanic-security/graphs/contributors
+[forks-shield]: https://img.shields.io/github/forks/sunset-developer/sanic-security.svg?style=flat-square
+[forks-url]: https://github.com/sunset-developer/sanic-security/network/members
+[stars-shield]: https://img.shields.io/github/stars/sunset-developer/sanic-security.svg?style=flat-square
+[stars-url]: https://github.com/sunset-developer/sanic-security/stargazers
+[issues-shield]: https://img.shields.io/github/issues/sunset-developer/sanic-security.svg?style=flat-square
+[issues-url]: https://github.com/sunset-developer/sanic-security/issues
+[license-shield]: https://img.shields.io/github/license/sunset-developer/sanic-security.svg?style=flat-square
+[license-url]: https://github.com/sunset-developer/sanic-security/blob/master/LICENSE
+
+
+%package -n python3-sanic-security
+Summary: An effective, simple, and async security library for the Sanic framework.
+Provides: python-sanic-security
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+BuildRequires: python3-pip
+%description -n python3-sanic-security
+<!-- PROJECT SHIELDS -->
+<!--
+*** I'm using markdown "reference style" links for readability.
+*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).
+*** See the bottom of this document for the declaration of the reference variables
+*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.
+*** https://www.markdownguide.org/basic-syntax/#reference-style-links
+-->
+
+[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
+[![Downloads](https://pepy.tech/badge/sanic-security)](https://pepy.tech/project/sanic-security)
+[![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/sanic-security.svg)](https://anaconda.org/conda-forge/sanic-security)
+
+
+<!-- PROJECT LOGO -->
+<br />
+<p align="center">
+ <h3 align="center">Sanic Security</h3>
+ <p align="center">
+ An effective, simple, and async security library for the Sanic framework.
+ </p>
+</p>
+
+
+<!-- TABLE OF CONTENTS -->
+## Table of Contents
+
+* [About the Project](#about-the-project)
+* [Getting Started](#getting-started)
+ * [Prerequisites](#prerequisites)
+ * [Installation](#installation)
+ * [Configuration](#configuration)
+* [Usage](#usage)
+ * [Authentication](#authentication)
+ * [Captcha](#captcha)
+ * [Two Step Verification](#two-step-verification)
+ * [Authorization](#authorization)
+ * [Testing](#testing)
+ * [Tortoise](#tortoise)
+* [Contributing](#contributing)
+* [License](#license)
+* [Versioning](#versioning)
+* [Support](https://discord.gg/JHpZkMfKTJ)
+
+<!-- ABOUT THE PROJECT -->
+## About The Project
+
+Sanic Security is an authentication, authorization, and verification library designed for use with [Sanic](https://github.com/huge-success/sanic).
+This library contains a variety of features including:
+
+* Login, registration, and authentication
+* Two-factor authentication
+* Captcha
+* Two-step verification
+* Role based authorization with wildcard permissions
+
+Please visit [security.na-stewart.com](https://security.na-stewart.com) for documentation.
+
+<!-- GETTING STARTED -->
+## Getting Started
+
+In order to get started, please install [Pip](https://pypi.org/).
+
+### Installation
+
+* Install the Sanic Security pip package.
+```shell
+pip3 install sanic-security
+````
+
+* Install the Sanic Security pip package with the `cryptography` dependency included.
+
+If you are planning on encoding or decoding JWTs using certain digital signature algorithms (like RSA or ECDSA which use
+the public secret and private secret), you will need to install the `cryptography` library. This can be installed explicitly, or
+as a required extra in the `sanic-security` requirement.
+
+```shell
+pip3 install sanic-security[crypto]
+````
+
+* For developers, fork Sanic Security and install development dependencies.
+```shell
+pip3 install -e ".[dev]"
+````
+
+### Configuration
+
+Sanic Security configuration is merely an object that can be modified either using dot-notation or like a
+dictionary.
+
+For example:
+
+```python
+from sanic_security.configuration import config
+
+config.SECRET = "This is a big secret. Shhhhh"
+config["CAPTCHA_FONT"] = "./resources/captcha.ttf"
+```
+
+You can also use the update() method like on regular dictionaries.
+
+Any environment variables defined with the SANIC_SECURITY_ prefix will be applied to the config. For example, setting
+SANIC_SECURITY_SECRET will be loaded by the application automatically and fed into the SECRET config variable.
+
+You can load environment variables with a different prefix via calling the `config.load_environment_variables("NEW_PREFIX_")` method.
+
+* Default configuration values:
+
+| Key | Value | Description |
+|---------------------------------------|------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
+| **SECRET** | This is a big secret. Shhhhh | The secret used for generating and signing JWTs. This should be a string unique to your application. Keep it safe. |
+| **PUBLIC_SECRET** | None | The secret used for verifying and decoding JWTs and can be publicly shared. This should be a string unique to your application. |
+| **SESSION_SAMESITE** | strict | The SameSite attribute of session cookies. |
+| **SESSION_SECURE** | True | The Secure attribute of session cookies. |
+| **SESSION_HTTPONLY** | True | The HttpOnly attribute of session cookies. HIGHLY recommended that you do not turn this off, unless you know what you are doing. |
+| **SESSION_DOMAIN** | None | The Domain attribute of session cookies. |
+| **SESSION_ENCODING_ALGORITHM** | HS256 | The algorithm used to encode and decode session JWT's. |
+| **SESSION_PREFIX** | token | Prefix attached to the beginning of session cookies. |
+| **MAX_CHALLENGE_ATTEMPTS** | 5 | The maximum amount of session challenge attempts allowed. |
+| **CAPTCHA_SESSION_EXPIRATION** | 60 | The amount of seconds till captcha session expiration on creation. Setting to 0 will disable expiration. |
+| **CAPTCHA_FONT** | captcha.ttf | The file path to the font being used for captcha generation. |
+| **TWO_STEP_SESSION_EXPIRATION** | 200 | The amount of seconds till two step session expiration on creation. Setting to 0 will disable expiration. |
+| **AUTHENTICATION_SESSION_EXPIRATION** | 2692000 | The amount of seconds till authentication session expiration on creation. Setting to 0 will disable expiration. |
+| **ALLOW_LOGIN_WITH_USERNAME** | False | Allows login via username and email. |
+| **INITIAL_ADMIN_EMAIL** | admin@example.com | Email used when creating the initial admin account. |
+| **INITIAL_ADMIN_PASSWORD** | admin123 | Password used when creating the initial admin account. |
+
+## Usage
+
+Sanic Security's authentication and verification functionality is session based. A new session will be created for the user after the user logs in or requests some form of verification (two-step, captcha). The session data is then encoded into a JWT and stored on a cookie on the user’s browser. The session cookie would be sent
+along with every subsequent request. The server can then compare the session stored on the cookie against the session information stored in the database to verify user’s identity and send a response with the corresponding state.
+
+The tables in the below examples represent example [request form-data](https://sanicframework.org/en/guide/basics/request.html#form).
+
+## Authentication
+
+* Initial Administrator Account
+
+This account can be logged into and has complete authoritative access. Login credentials can be modified in config.
+
+ ```python
+ create_initial_admin_account(app)
+ if __name__ == "__main__":
+ app.run(host="127.0.0.1", port=8000)
+ ```
+
+* Registration (With Two-step Verification)
+
+Phone can be null or empty.
+
+| Key | Value |
+|--------------|---------------------|
+| **username** | example |
+| **email** | example@example.com |
+| **phone** | 19811354186 |
+| **password** | examplepass |
+
+```python
+@app.post("api/security/register")
+async def on_register(request):
+ account = await register(request)
+ two_step_session = await request_two_step_verification(request, account)
+ await email_code(
+ account.email, two_step_session.code # Code = AJ8HGD
+ ) # Custom method for emailing verification code.
+ response = json(
+ "Registration successful! Email verification required.",
+ two_step_session.bearer.json,
+ )
+ two_step_session.encode(response)
+ return response
+```
+
+* Verify Account
+
+Verifies the client's account via two-step session code.
+
+| Key | Value |
+|----------|--------|
+| **code** | AJ8HGD |
+
+```python
+@app.post("api/security/verify")
+async def on_verify(request):
+ two_step_session = await verify_account(request)
+ return json(
+ "You have verified your account and may login!", two_step_session.bearer.json
+ )
+```
+
+* Login (With Two-factor Authentication)
+
+Login credentials are retrieved via the Authorization header. Credentials are constructed by first combining the
+username and the password with a colon (aladdin:opensesame), and then by encoding the resulting string in base64
+(YWxhZGRpbjpvcGVuc2VzYW1l). Here is an example authorization header: `Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l`.
+
+You can use a username as well as an email for login if `ALLOW_LOGIN_WITH_USERNAME` is true in the config.
+
+```python
+@app.post("api/security/login")
+async def on_login(request):
+ authentication_session = await login(request, require_second_factor=True)
+ two_step_session = await request_two_step_verification(
+ request, authentication_session.bearer
+ )
+ await email_code(
+ authentication_session.bearer.email, two_step_session.code # Code = BG5KLP
+ ) # Custom method for emailing verification code.
+ response = json(
+ "Login successful! Two-factor authentication required.",
+ authentication_session.bearer.json,
+ )
+ authentication_session.encode(response)
+ two_step_session.encode(response)
+ return response
+```
+
+* Fulfill Second Factor
+
+Fulfills client authentication session's second factor requirement via two-step session code.
+
+| Key | Value |
+|----------|--------|
+| **code** | BG5KLP |
+
+```python
+@app.post("api/security/validate-2fa")
+async def on_two_factor_authentication(request):
+ authentication_session = await fulfill_second_factor(request)
+ response = json(
+ "Authentication session second-factor fulfilled! You are now authenticated.",
+ authentication_session.bearer.json,
+ )
+ authentication_session.encode(response)
+ return response
+```
+
+* Logout
+
+```python
+@app.post("api/security/logout")
+async def on_logout(request):
+ authentication_session = await logout(request)
+ response = json("Logout successful!", authentication_session.bearer.json)
+ return response
+```
+
+* Authenticate
+
+```python
+@app.post("api/security/auth")
+async def on_authenticate(request):
+ authentication_session = await authenticate(request)
+ return json(
+ "You have been authenticated.",
+ authentication_session.bearer.json,
+ )
+```
+
+* Requires Authentication (This method is not called directly and instead used as a decorator.)
+
+```python
+@app.post("api/security/auth")
+@requires_authentication()
+async def on_authenticate(request, authentication_session):
+ return json(
+ "You have been authenticated.",
+ authentication_session.bearer.json,
+ )
+```
+
+## Captcha
+
+A pre-existing font for captcha challenges is included in the Sanic Security repository. You may set your own font by
+downloading a .ttf font and defining the file's path in the configuration.
+
+[1001 Free Fonts](https://www.1001fonts.com/)
+
+[Recommended Font](https://www.1001fonts.com/source-sans-pro-font.html)
+
+Captcha challenge example:
+
+[![Captcha image.](https://github.com/sunset-developer/sanic-security/blob/main/images/captcha.png)](https://github.com/sunset-developer/sanic-security/blob/main/images/captcha.png)
+
+* Request Captcha
+
+```python
+@app.get("api/security/captcha")
+async def on_captcha_img_request(request):
+ captcha_session = await request_captcha(request)
+ response = captcha_session.get_image() # Captcha: FV9NMQ
+ captcha_session.encode(response)
+ return response
+```
+
+* Captcha
+
+| Key | Value |
+|-------------|--------|
+| **captcha** | FV9NMQ |
+
+```python
+@app.post("api/security/captcha")
+async def on_captcha(request):
+ captcha_session = await captcha(request)
+ return json("Captcha attempt successful!", captcha_session.json)
+```
+
+* Requires Captcha (This method is not called directly and instead used as a decorator.)
+
+| Key | Value |
+|-------------|--------|
+| **captcha** | FV9NMQ |
+
+```python
+@app.post("ap/security/captcha")
+@requires_captcha()
+async def on_captcha(request, captcha_session):
+ return json("Captcha attempt successful!", captcha_session.json)
+```
+
+## Two-step Verification
+
+Two-step verification should be integrated with other custom functionality. For example, account verification during registration.
+
+* Request Two-step Verification
+
+| Key | Value |
+|-------------|---------------------|
+| **email** | example@example.com |
+
+```python
+@app.post("api/security/two-step/request")
+async def on_two_step_request(request):
+ two_step_session = await request_two_step_verification(request)
+ await email_code(
+ account.email, two_step_session.code # Code = DT6JZX
+ ) # Custom method for emailing verification code.
+ response = json("Verification request successful!", two_step_session.bearer.json)
+ two_step_session.encode(response)
+ return response
+```
+
+* Resend Two-step Verification Code
+
+```python
+@app.post("api/security/two-step/resend")
+async def on_two_step_resend(request):
+ two_step_session = await TwoStepSession.decode(request)
+ await email_code(
+ account.email, two_step_session.code # Code = DT6JZX
+ ) # Custom method for emailing verification code.
+ return json("Verification code resend successful!", two_step_session.bearer.json)
+```
+
+* Two-step Verification
+
+| Key | Value |
+|----------|--------|
+| **code** | DT6JZX |
+
+```python
+@app.post("api/security/two-step")
+async def on_two_step_verification(request):
+ two_step_session = await two_step_verification(request)
+ response = json(
+ "Two-step verification attempt successful!", two_step_session.bearer.json
+ )
+ return response
+```
+
+* Requires Two-step Verification (This method is not called directly and instead used as a decorator.)
+
+| Key | Value |
+|----------|--------|
+| **code** | DT6JZX |
+
+```python
+@app.post("api/security/two-step")
+@requires_two_step_verification()
+async def on_two_step_verification(request, two_step_session):
+ response = json(
+ "Two-step verification attempt successful!", two_step_session.bearer.json
+ )
+ return response
+```
+
+## Authorization
+
+Sanic Security uses role based authorization with wildcard permissions.
+
+Roles are created for various job functions. The permissions to perform certain operations are assigned to specific roles.
+Users are assigned particular roles, and through those role assignments acquire the permissions needed to perform
+particular system functions. Since users are not assigned permissions directly, but only acquire them through their
+role (or roles), management of individual user rights becomes a matter of simply assigning appropriate roles to the
+user's account; this simplifies common operations, such as adding a user, or changing a user's department.
+
+Wildcard permissions support the concept of multiple levels or parts. For example, you could grant a user the permission
+`printer:query`, `printer:query,delete`, or `printer:*`.
+* Assign Role
+
+```python
+await assign_role(
+ "Chat Room Moderator",
+ account,
+ "channels:view,delete, account:suspend,mute, voice:*",
+ "Can read and delete messages in all chat rooms, suspend and mute accounts, and control voice chat.",
+)
+```
+
+* Check Permissions
+
+```python
+@app.post("api/security/perms")
+async def on_check_perms(request):
+ authentication_session = await check_permissions(
+ request, "channels:view", "voice:*"
+ )
+ return text("Account is authorized.")
+```
+
+
+* Require Permissions (This method is not called directly and instead used as a decorator.)
+
+```python
+@app.post("api/security/perms")
+@require_permissions("channels:view", "voice:*")
+async def on_check_perms(request, authentication_session):
+ return text("Account is authorized.")
+```
+
+* Check Roles
+
+```python
+@app.post("api/security/roles")
+async def on_check_roles(request):
+ authentication_session = await check_roles(request, "Chat Room Moderator")
+ return text("Account is authorized.")
+```
+
+* Require Roles (This method is not called directly and instead used as a decorator.)
+
+```python
+@app.post("api/security/roles")
+@require_roles("Chat Room Moderator")
+async def on_check_roles(request, authentication_session):
+ return text("Account is authorized.")
+```
+
+## Testing
+
+* Set the `TEST_DATABASE_URL` configuration value.
+
+* Make sure the test Sanic instance (`test/server.py`) is running on your machine.
+
+* Run the unit test client (`test/tests.py`) for results.
+
+## Tortoise
+
+Sanic Security uses [Tortoise ORM](https://tortoise-orm.readthedocs.io/en/latest/index.html) for database operations.
+
+Tortoise ORM is an easy-to-use asyncio ORM (Object Relational Mapper).
+
+* Initialise your models and database like so:
+
+```python
+async def init():
+ await Tortoise.init(
+ db_url="sqlite://db.sqlite3",
+ modules={"models": ["sanic_security.models", "app.models"]},
+ )
+ await Tortoise.generate_schemas()
+```
+
+or
+
+```python
+register_tortoise(
+ app,
+ db_url="sqlite://db.sqlite3",
+ modules={"models": ["sanic_security.models", "app.models"]},
+ generate_schemas=True,
+)
+```
+
+* Define your models like so:
+
+```python
+from tortoise.models import Model
+from tortoise import fields
+
+
+class Tournament(Model):
+ id = fields.IntField(pk=True)
+ name = fields.TextField()
+```
+
+* Use it like so:
+
+```python
+# Create instance by save
+tournament = Tournament(name="New Tournament")
+await tournament.save()
+
+# Or by .create()
+await Tournament.create(name="Another Tournament")
+
+# Now search for a record
+tour = await Tournament.filter(name__contains="Another").first()
+print(tour.name)
+```
+
+<!-- CONTRIBUTING -->
+## Contributing
+
+Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
+
+1. Fork the Project
+2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
+3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
+4. Push to the Branch (`git push origin feature/AmazingFeature`)
+5. Open a Pull Request
+
+
+<!-- LICENSE -->
+## License
+
+Distributed under the GNU Affero General Public License v3.0. See `LICENSE` for more information.
+
+<!-- Versioning -->
+## Versioning
+
+**0.0.0**
+
+* MAJOR version when you make incompatible API changes.
+
+* MINOR version when you add functionality in a backwards compatible manner.
+
+* PATCH version when you make backwards compatible bug fixes.
+
+[https://semver.org/](https://semver.org/)
+
+<!-- MARKDOWN LINKS & IMAGES -->
+<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
+[contributors-shield]: https://img.shields.io/github/contributors/sunset-developer/sanic-security.svg?style=flat-square
+[contributors-url]: https://github.com/sunset-developer/sanic-security/graphs/contributors
+[forks-shield]: https://img.shields.io/github/forks/sunset-developer/sanic-security.svg?style=flat-square
+[forks-url]: https://github.com/sunset-developer/sanic-security/network/members
+[stars-shield]: https://img.shields.io/github/stars/sunset-developer/sanic-security.svg?style=flat-square
+[stars-url]: https://github.com/sunset-developer/sanic-security/stargazers
+[issues-shield]: https://img.shields.io/github/issues/sunset-developer/sanic-security.svg?style=flat-square
+[issues-url]: https://github.com/sunset-developer/sanic-security/issues
+[license-shield]: https://img.shields.io/github/license/sunset-developer/sanic-security.svg?style=flat-square
+[license-url]: https://github.com/sunset-developer/sanic-security/blob/master/LICENSE
+
+
+%package help
+Summary: Development documents and examples for sanic-security
+Provides: python3-sanic-security-doc
+%description help
+<!-- PROJECT SHIELDS -->
+<!--
+*** I'm using markdown "reference style" links for readability.
+*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).
+*** See the bottom of this document for the declaration of the reference variables
+*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.
+*** https://www.markdownguide.org/basic-syntax/#reference-style-links
+-->
+
+[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
+[![Downloads](https://pepy.tech/badge/sanic-security)](https://pepy.tech/project/sanic-security)
+[![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/sanic-security.svg)](https://anaconda.org/conda-forge/sanic-security)
+
+
+<!-- PROJECT LOGO -->
+<br />
+<p align="center">
+ <h3 align="center">Sanic Security</h3>
+ <p align="center">
+ An effective, simple, and async security library for the Sanic framework.
+ </p>
+</p>
+
+
+<!-- TABLE OF CONTENTS -->
+## Table of Contents
+
+* [About the Project](#about-the-project)
+* [Getting Started](#getting-started)
+ * [Prerequisites](#prerequisites)
+ * [Installation](#installation)
+ * [Configuration](#configuration)
+* [Usage](#usage)
+ * [Authentication](#authentication)
+ * [Captcha](#captcha)
+ * [Two Step Verification](#two-step-verification)
+ * [Authorization](#authorization)
+ * [Testing](#testing)
+ * [Tortoise](#tortoise)
+* [Contributing](#contributing)
+* [License](#license)
+* [Versioning](#versioning)
+* [Support](https://discord.gg/JHpZkMfKTJ)
+
+<!-- ABOUT THE PROJECT -->
+## About The Project
+
+Sanic Security is an authentication, authorization, and verification library designed for use with [Sanic](https://github.com/huge-success/sanic).
+This library contains a variety of features including:
+
+* Login, registration, and authentication
+* Two-factor authentication
+* Captcha
+* Two-step verification
+* Role based authorization with wildcard permissions
+
+Please visit [security.na-stewart.com](https://security.na-stewart.com) for documentation.
+
+<!-- GETTING STARTED -->
+## Getting Started
+
+In order to get started, please install [Pip](https://pypi.org/).
+
+### Installation
+
+* Install the Sanic Security pip package.
+```shell
+pip3 install sanic-security
+````
+
+* Install the Sanic Security pip package with the `cryptography` dependency included.
+
+If you are planning on encoding or decoding JWTs using certain digital signature algorithms (like RSA or ECDSA which use
+the public secret and private secret), you will need to install the `cryptography` library. This can be installed explicitly, or
+as a required extra in the `sanic-security` requirement.
+
+```shell
+pip3 install sanic-security[crypto]
+````
+
+* For developers, fork Sanic Security and install development dependencies.
+```shell
+pip3 install -e ".[dev]"
+````
+
+### Configuration
+
+Sanic Security configuration is merely an object that can be modified either using dot-notation or like a
+dictionary.
+
+For example:
+
+```python
+from sanic_security.configuration import config
+
+config.SECRET = "This is a big secret. Shhhhh"
+config["CAPTCHA_FONT"] = "./resources/captcha.ttf"
+```
+
+You can also use the update() method like on regular dictionaries.
+
+Any environment variables defined with the SANIC_SECURITY_ prefix will be applied to the config. For example, setting
+SANIC_SECURITY_SECRET will be loaded by the application automatically and fed into the SECRET config variable.
+
+You can load environment variables with a different prefix via calling the `config.load_environment_variables("NEW_PREFIX_")` method.
+
+* Default configuration values:
+
+| Key | Value | Description |
+|---------------------------------------|------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
+| **SECRET** | This is a big secret. Shhhhh | The secret used for generating and signing JWTs. This should be a string unique to your application. Keep it safe. |
+| **PUBLIC_SECRET** | None | The secret used for verifying and decoding JWTs and can be publicly shared. This should be a string unique to your application. |
+| **SESSION_SAMESITE** | strict | The SameSite attribute of session cookies. |
+| **SESSION_SECURE** | True | The Secure attribute of session cookies. |
+| **SESSION_HTTPONLY** | True | The HttpOnly attribute of session cookies. HIGHLY recommended that you do not turn this off, unless you know what you are doing. |
+| **SESSION_DOMAIN** | None | The Domain attribute of session cookies. |
+| **SESSION_ENCODING_ALGORITHM** | HS256 | The algorithm used to encode and decode session JWT's. |
+| **SESSION_PREFIX** | token | Prefix attached to the beginning of session cookies. |
+| **MAX_CHALLENGE_ATTEMPTS** | 5 | The maximum amount of session challenge attempts allowed. |
+| **CAPTCHA_SESSION_EXPIRATION** | 60 | The amount of seconds till captcha session expiration on creation. Setting to 0 will disable expiration. |
+| **CAPTCHA_FONT** | captcha.ttf | The file path to the font being used for captcha generation. |
+| **TWO_STEP_SESSION_EXPIRATION** | 200 | The amount of seconds till two step session expiration on creation. Setting to 0 will disable expiration. |
+| **AUTHENTICATION_SESSION_EXPIRATION** | 2692000 | The amount of seconds till authentication session expiration on creation. Setting to 0 will disable expiration. |
+| **ALLOW_LOGIN_WITH_USERNAME** | False | Allows login via username and email. |
+| **INITIAL_ADMIN_EMAIL** | admin@example.com | Email used when creating the initial admin account. |
+| **INITIAL_ADMIN_PASSWORD** | admin123 | Password used when creating the initial admin account. |
+
+## Usage
+
+Sanic Security's authentication and verification functionality is session based. A new session will be created for the user after the user logs in or requests some form of verification (two-step, captcha). The session data is then encoded into a JWT and stored on a cookie on the user’s browser. The session cookie would be sent
+along with every subsequent request. The server can then compare the session stored on the cookie against the session information stored in the database to verify user’s identity and send a response with the corresponding state.
+
+The tables in the below examples represent example [request form-data](https://sanicframework.org/en/guide/basics/request.html#form).
+
+## Authentication
+
+* Initial Administrator Account
+
+This account can be logged into and has complete authoritative access. Login credentials can be modified in config.
+
+ ```python
+ create_initial_admin_account(app)
+ if __name__ == "__main__":
+ app.run(host="127.0.0.1", port=8000)
+ ```
+
+* Registration (With Two-step Verification)
+
+Phone can be null or empty.
+
+| Key | Value |
+|--------------|---------------------|
+| **username** | example |
+| **email** | example@example.com |
+| **phone** | 19811354186 |
+| **password** | examplepass |
+
+```python
+@app.post("api/security/register")
+async def on_register(request):
+ account = await register(request)
+ two_step_session = await request_two_step_verification(request, account)
+ await email_code(
+ account.email, two_step_session.code # Code = AJ8HGD
+ ) # Custom method for emailing verification code.
+ response = json(
+ "Registration successful! Email verification required.",
+ two_step_session.bearer.json,
+ )
+ two_step_session.encode(response)
+ return response
+```
+
+* Verify Account
+
+Verifies the client's account via two-step session code.
+
+| Key | Value |
+|----------|--------|
+| **code** | AJ8HGD |
+
+```python
+@app.post("api/security/verify")
+async def on_verify(request):
+ two_step_session = await verify_account(request)
+ return json(
+ "You have verified your account and may login!", two_step_session.bearer.json
+ )
+```
+
+* Login (With Two-factor Authentication)
+
+Login credentials are retrieved via the Authorization header. Credentials are constructed by first combining the
+username and the password with a colon (aladdin:opensesame), and then by encoding the resulting string in base64
+(YWxhZGRpbjpvcGVuc2VzYW1l). Here is an example authorization header: `Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l`.
+
+You can use a username as well as an email for login if `ALLOW_LOGIN_WITH_USERNAME` is true in the config.
+
+```python
+@app.post("api/security/login")
+async def on_login(request):
+ authentication_session = await login(request, require_second_factor=True)
+ two_step_session = await request_two_step_verification(
+ request, authentication_session.bearer
+ )
+ await email_code(
+ authentication_session.bearer.email, two_step_session.code # Code = BG5KLP
+ ) # Custom method for emailing verification code.
+ response = json(
+ "Login successful! Two-factor authentication required.",
+ authentication_session.bearer.json,
+ )
+ authentication_session.encode(response)
+ two_step_session.encode(response)
+ return response
+```
+
+* Fulfill Second Factor
+
+Fulfills client authentication session's second factor requirement via two-step session code.
+
+| Key | Value |
+|----------|--------|
+| **code** | BG5KLP |
+
+```python
+@app.post("api/security/validate-2fa")
+async def on_two_factor_authentication(request):
+ authentication_session = await fulfill_second_factor(request)
+ response = json(
+ "Authentication session second-factor fulfilled! You are now authenticated.",
+ authentication_session.bearer.json,
+ )
+ authentication_session.encode(response)
+ return response
+```
+
+* Logout
+
+```python
+@app.post("api/security/logout")
+async def on_logout(request):
+ authentication_session = await logout(request)
+ response = json("Logout successful!", authentication_session.bearer.json)
+ return response
+```
+
+* Authenticate
+
+```python
+@app.post("api/security/auth")
+async def on_authenticate(request):
+ authentication_session = await authenticate(request)
+ return json(
+ "You have been authenticated.",
+ authentication_session.bearer.json,
+ )
+```
+
+* Requires Authentication (This method is not called directly and instead used as a decorator.)
+
+```python
+@app.post("api/security/auth")
+@requires_authentication()
+async def on_authenticate(request, authentication_session):
+ return json(
+ "You have been authenticated.",
+ authentication_session.bearer.json,
+ )
+```
+
+## Captcha
+
+A pre-existing font for captcha challenges is included in the Sanic Security repository. You may set your own font by
+downloading a .ttf font and defining the file's path in the configuration.
+
+[1001 Free Fonts](https://www.1001fonts.com/)
+
+[Recommended Font](https://www.1001fonts.com/source-sans-pro-font.html)
+
+Captcha challenge example:
+
+[![Captcha image.](https://github.com/sunset-developer/sanic-security/blob/main/images/captcha.png)](https://github.com/sunset-developer/sanic-security/blob/main/images/captcha.png)
+
+* Request Captcha
+
+```python
+@app.get("api/security/captcha")
+async def on_captcha_img_request(request):
+ captcha_session = await request_captcha(request)
+ response = captcha_session.get_image() # Captcha: FV9NMQ
+ captcha_session.encode(response)
+ return response
+```
+
+* Captcha
+
+| Key | Value |
+|-------------|--------|
+| **captcha** | FV9NMQ |
+
+```python
+@app.post("api/security/captcha")
+async def on_captcha(request):
+ captcha_session = await captcha(request)
+ return json("Captcha attempt successful!", captcha_session.json)
+```
+
+* Requires Captcha (This method is not called directly and instead used as a decorator.)
+
+| Key | Value |
+|-------------|--------|
+| **captcha** | FV9NMQ |
+
+```python
+@app.post("ap/security/captcha")
+@requires_captcha()
+async def on_captcha(request, captcha_session):
+ return json("Captcha attempt successful!", captcha_session.json)
+```
+
+## Two-step Verification
+
+Two-step verification should be integrated with other custom functionality. For example, account verification during registration.
+
+* Request Two-step Verification
+
+| Key | Value |
+|-------------|---------------------|
+| **email** | example@example.com |
+
+```python
+@app.post("api/security/two-step/request")
+async def on_two_step_request(request):
+ two_step_session = await request_two_step_verification(request)
+ await email_code(
+ account.email, two_step_session.code # Code = DT6JZX
+ ) # Custom method for emailing verification code.
+ response = json("Verification request successful!", two_step_session.bearer.json)
+ two_step_session.encode(response)
+ return response
+```
+
+* Resend Two-step Verification Code
+
+```python
+@app.post("api/security/two-step/resend")
+async def on_two_step_resend(request):
+ two_step_session = await TwoStepSession.decode(request)
+ await email_code(
+ account.email, two_step_session.code # Code = DT6JZX
+ ) # Custom method for emailing verification code.
+ return json("Verification code resend successful!", two_step_session.bearer.json)
+```
+
+* Two-step Verification
+
+| Key | Value |
+|----------|--------|
+| **code** | DT6JZX |
+
+```python
+@app.post("api/security/two-step")
+async def on_two_step_verification(request):
+ two_step_session = await two_step_verification(request)
+ response = json(
+ "Two-step verification attempt successful!", two_step_session.bearer.json
+ )
+ return response
+```
+
+* Requires Two-step Verification (This method is not called directly and instead used as a decorator.)
+
+| Key | Value |
+|----------|--------|
+| **code** | DT6JZX |
+
+```python
+@app.post("api/security/two-step")
+@requires_two_step_verification()
+async def on_two_step_verification(request, two_step_session):
+ response = json(
+ "Two-step verification attempt successful!", two_step_session.bearer.json
+ )
+ return response
+```
+
+## Authorization
+
+Sanic Security uses role based authorization with wildcard permissions.
+
+Roles are created for various job functions. The permissions to perform certain operations are assigned to specific roles.
+Users are assigned particular roles, and through those role assignments acquire the permissions needed to perform
+particular system functions. Since users are not assigned permissions directly, but only acquire them through their
+role (or roles), management of individual user rights becomes a matter of simply assigning appropriate roles to the
+user's account; this simplifies common operations, such as adding a user, or changing a user's department.
+
+Wildcard permissions support the concept of multiple levels or parts. For example, you could grant a user the permission
+`printer:query`, `printer:query,delete`, or `printer:*`.
+* Assign Role
+
+```python
+await assign_role(
+ "Chat Room Moderator",
+ account,
+ "channels:view,delete, account:suspend,mute, voice:*",
+ "Can read and delete messages in all chat rooms, suspend and mute accounts, and control voice chat.",
+)
+```
+
+* Check Permissions
+
+```python
+@app.post("api/security/perms")
+async def on_check_perms(request):
+ authentication_session = await check_permissions(
+ request, "channels:view", "voice:*"
+ )
+ return text("Account is authorized.")
+```
+
+
+* Require Permissions (This method is not called directly and instead used as a decorator.)
+
+```python
+@app.post("api/security/perms")
+@require_permissions("channels:view", "voice:*")
+async def on_check_perms(request, authentication_session):
+ return text("Account is authorized.")
+```
+
+* Check Roles
+
+```python
+@app.post("api/security/roles")
+async def on_check_roles(request):
+ authentication_session = await check_roles(request, "Chat Room Moderator")
+ return text("Account is authorized.")
+```
+
+* Require Roles (This method is not called directly and instead used as a decorator.)
+
+```python
+@app.post("api/security/roles")
+@require_roles("Chat Room Moderator")
+async def on_check_roles(request, authentication_session):
+ return text("Account is authorized.")
+```
+
+## Testing
+
+* Set the `TEST_DATABASE_URL` configuration value.
+
+* Make sure the test Sanic instance (`test/server.py`) is running on your machine.
+
+* Run the unit test client (`test/tests.py`) for results.
+
+## Tortoise
+
+Sanic Security uses [Tortoise ORM](https://tortoise-orm.readthedocs.io/en/latest/index.html) for database operations.
+
+Tortoise ORM is an easy-to-use asyncio ORM (Object Relational Mapper).
+
+* Initialise your models and database like so:
+
+```python
+async def init():
+ await Tortoise.init(
+ db_url="sqlite://db.sqlite3",
+ modules={"models": ["sanic_security.models", "app.models"]},
+ )
+ await Tortoise.generate_schemas()
+```
+
+or
+
+```python
+register_tortoise(
+ app,
+ db_url="sqlite://db.sqlite3",
+ modules={"models": ["sanic_security.models", "app.models"]},
+ generate_schemas=True,
+)
+```
+
+* Define your models like so:
+
+```python
+from tortoise.models import Model
+from tortoise import fields
+
+
+class Tournament(Model):
+ id = fields.IntField(pk=True)
+ name = fields.TextField()
+```
+
+* Use it like so:
+
+```python
+# Create instance by save
+tournament = Tournament(name="New Tournament")
+await tournament.save()
+
+# Or by .create()
+await Tournament.create(name="Another Tournament")
+
+# Now search for a record
+tour = await Tournament.filter(name__contains="Another").first()
+print(tour.name)
+```
+
+<!-- CONTRIBUTING -->
+## Contributing
+
+Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
+
+1. Fork the Project
+2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
+3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
+4. Push to the Branch (`git push origin feature/AmazingFeature`)
+5. Open a Pull Request
+
+
+<!-- LICENSE -->
+## License
+
+Distributed under the GNU Affero General Public License v3.0. See `LICENSE` for more information.
+
+<!-- Versioning -->
+## Versioning
+
+**0.0.0**
+
+* MAJOR version when you make incompatible API changes.
+
+* MINOR version when you add functionality in a backwards compatible manner.
+
+* PATCH version when you make backwards compatible bug fixes.
+
+[https://semver.org/](https://semver.org/)
+
+<!-- MARKDOWN LINKS & IMAGES -->
+<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
+[contributors-shield]: https://img.shields.io/github/contributors/sunset-developer/sanic-security.svg?style=flat-square
+[contributors-url]: https://github.com/sunset-developer/sanic-security/graphs/contributors
+[forks-shield]: https://img.shields.io/github/forks/sunset-developer/sanic-security.svg?style=flat-square
+[forks-url]: https://github.com/sunset-developer/sanic-security/network/members
+[stars-shield]: https://img.shields.io/github/stars/sunset-developer/sanic-security.svg?style=flat-square
+[stars-url]: https://github.com/sunset-developer/sanic-security/stargazers
+[issues-shield]: https://img.shields.io/github/issues/sunset-developer/sanic-security.svg?style=flat-square
+[issues-url]: https://github.com/sunset-developer/sanic-security/issues
+[license-shield]: https://img.shields.io/github/license/sunset-developer/sanic-security.svg?style=flat-square
+[license-url]: https://github.com/sunset-developer/sanic-security/blob/master/LICENSE
+
+
+%prep
+%autosetup -n sanic-security-1.10.3
+
+%build
+%py3_build
+
+%install
+%py3_install
+install -d -m755 %{buildroot}/%{_pkgdocdir}
+if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
+if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
+if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
+if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
+pushd %{buildroot}
+if [ -d usr/lib ]; then
+ find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/lib64 ]; then
+ find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/bin ]; then
+ find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/sbin ]; then
+ find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+touch doclist.lst
+if [ -d usr/share/man ]; then
+ find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
+fi
+popd
+mv %{buildroot}/filelist.lst .
+mv %{buildroot}/doclist.lst .
+
+%files -n python3-sanic-security -f filelist.lst
+%dir %{python3_sitelib}/*
+
+%files help -f doclist.lst
+%{_docdir}/*
+
+%changelog
+* Mon May 15 2023 Python_Bot <Python_Bot@openeuler.org> - 1.10.3-1
+- Package Spec generated
diff --git a/sources b/sources
new file mode 100644
index 0000000..6fc69e0
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+06f9486112703f24660e9bb46404f9f0 sanic-security-1.10.3.tar.gz