From b6dec8f4028df6a7d8a0a58ab7bc5e608e7046eb Mon Sep 17 00:00:00 2001 From: CoprDistGit Date: Mon, 15 May 2023 03:24:55 +0000 Subject: automatic import of python-sanic-security --- .gitignore | 1 + python-sanic-security.spec | 1734 ++++++++++++++++++++++++++++++++++++++++++++ sources | 1 + 3 files changed, 1736 insertions(+) create mode 100644 python-sanic-security.spec create mode 100644 sources 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 + + + +[![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) + + + +
+

+

Sanic Security

+

+ An effective, simple, and async security library for the Sanic framework. +

+

+ + + +## 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 + +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 + +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 + +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 + +Distributed under the GNU Affero General Public License v3.0. See `LICENSE` for more information. + + +## 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/) + + + +[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 + + + +[![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) + + + +
+

+

Sanic Security

+

+ An effective, simple, and async security library for the Sanic framework. +

+

+ + + +## 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 + +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 + +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 + +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 + +Distributed under the GNU Affero General Public License v3.0. See `LICENSE` for more information. + + +## 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/) + + + +[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 + + + +[![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) + + + +
+

+

Sanic Security

+

+ An effective, simple, and async security library for the Sanic framework. +

+

+ + + +## 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 + +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 + +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 + +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 + +Distributed under the GNU Affero General Public License v3.0. See `LICENSE` for more information. + + +## 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/) + + + +[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 - 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 -- cgit v1.2.3