-
-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
43 changed files
with
1,478 additions
and
158 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import os | ||
|
||
alias kkk os.system('kill -9 %d' % os.getpid()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
# Example | ||
|
||
Since [Edgy](https://edgy.tarsild.io) if from the same author of Esmerald, it gives some | ||
extra motivation for its use and therefore an example in how to use the | ||
[JWTAuthMiddleware](./middleware.md), even if in a very simplistic, within your Esmerald application. | ||
|
||
|
||
Let us build a simple integration and application where we will be creating: | ||
|
||
- [Create user model](#create-user-model) by using the provided default from Esmerald. | ||
- [Create user API](#create-user-api) to create a user in the system. | ||
- [Login API](#login-api) to authenticate the user. | ||
- [Home API](#home-api) to authenticate the user and return the logged-in user email. | ||
- [Assemble the apis](#assemble-the-apis) where we wrap the application. | ||
|
||
We will be using SQLite for this example but feel free to integrate with your database. | ||
|
||
We will also be assuming the following: | ||
|
||
- Models are inside an `accounts/models.py` | ||
- Views/APIs are inside an `accounts/views.py` | ||
- The main application is inside an `app.py` | ||
- The [jwt_config](../../configurations/jwt.md#jwtconfig-and-application-settings) | ||
is inside your global [settings](../../application/settings.md). | ||
|
||
**Lets go!** | ||
|
||
## Create user model | ||
|
||
First, we need to create a model that will be storing the users in the system. We will be | ||
defaulting to the one model provided by Esmerald out-of-the-box. | ||
|
||
```python title="accounts/models.py" | ||
{!> ../docs_src/databases/edgy/example/create_model.py !} | ||
``` | ||
|
||
## Create user API | ||
|
||
Now that the [user model](#create-user-model) is defined and created, time to create an api | ||
that allows the creation of users in the system. | ||
|
||
This example won't cover corner cases like integrity in ase of duplicates and so on as this is | ||
something that you can easily manage. | ||
|
||
```python title="accounts/views.py" | ||
{!> ../docs_src/databases/edgy/example/create_user.py !} | ||
``` | ||
|
||
## Login API | ||
|
||
Now the [create user](#create-user-api) is available to us to be used later on, we need a view | ||
that also allow us to login and return the JWT access token. | ||
|
||
For this API to work, we need to garantee the data being sent is valid, authenticate and then | ||
return the JWT token. | ||
|
||
```python title="accounts/views.py" | ||
{!> ../docs_src/databases/edgy/example/login.py !} | ||
``` | ||
|
||
Ooof! There is a lot going on here right? Well, yes but this is also intentional. The `login` | ||
is actually very simple, it just receives a payload and throws that payload into validation | ||
inside the `BackendAuthentication`. | ||
|
||
For those familiar with similar objects, like Django backends, this `BackendAuthentication` does | ||
roughly the same thing and it is quite robust since it is using pydantic when creating the instance | ||
which takes advantage of the validations automatically for you. | ||
|
||
The `BackendAuthentication` once created inside the `login` and validated with the given fields, | ||
simply proceeds with the `authenticate` method where it will return the JWT for the user. | ||
|
||
!!! Warning | ||
As mentioned before in the assumptions on the top of the document, it was assumed you put your | ||
[jwt_config](../../configurations/jwt.md#jwtconfig-and-application-settings) inside your global settings. | ||
|
||
## Home API | ||
|
||
Now it is time to create the api that will be returning the email of the logged in user when hit. | ||
The API is pretty much simple and clean. | ||
|
||
```python title="accounts/views.py" | ||
{!> ../docs_src/databases/edgy/example/home.py !} | ||
``` | ||
|
||
## Assemble the APIs | ||
|
||
Now it the time where we assemble everything in one place and create our Esmerald application. | ||
|
||
```python title="app.py" | ||
{!> ../docs_src/databases/edgy/example/assemble.py !} | ||
``` | ||
|
||
Did you notice the import of the `JWTAuthMiddleware` is inside the | ||
[Include](../../routing/routes.md#include) and not in the main Esmerald instance? | ||
|
||
**It is intentional!** Each include handles its own middlewares and to create a user and login | ||
you **don't want to be logged-in** and for that reason, the `JWTAuthMiddleware` is only for those | ||
endpoints that **require authentication**. | ||
|
||
Now this assembling is actually very clean, right? Yes and the reason for that is because Esmerald | ||
itself promotes clean design. | ||
|
||
We have imported all the APIs directly in the `app.py` but this **is not mandatory**. You can | ||
take advantage of the [Include](../../routing/routes.md#include) and clean your application | ||
even more. | ||
|
||
## Extra | ||
|
||
Come on, give it a try, create your own version and then try to access the `home`. | ||
|
||
Let us see how we could access `/` using the current setup. | ||
|
||
For this will be using `httpx` but you are free to use whatever client you prefer. | ||
|
||
### Steps | ||
|
||
1. Create a user. | ||
2. Login and get the jwt token. | ||
3. Access the home `/`. | ||
|
||
```python | ||
{!> ../docs_src/databases/edgy/example/access.py !} | ||
``` | ||
|
||
## Conclusions | ||
|
||
This is just a simple example how you could use Edgy with the provided `JWTAuthMiddleware` | ||
from **Esmerald** and build a quick, yet robust, login system and access protected APIs. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# Middleware | ||
|
||
As part of the support, Esmerald developed an authentication middleware using python-jose allowing JWT integration | ||
with the current [supported models](./models.md#user). | ||
|
||
## JWTAuthMiddleware | ||
|
||
This simple but effective middleware extends the [BaseAuthMiddleware](../../middleware/middleware.md#baseauthmiddleware) | ||
and enables the authentication via JWT. | ||
|
||
```python | ||
from esmerald.contrib.auth.edgy.middleware import JWTAuthMiddleware | ||
``` | ||
|
||
### Parameters | ||
|
||
* `app` - Any ASGI app instance. E.g.: Esmerald instance. | ||
* `config` - An instance of [JWTConfig](../../configurations/jwt.md) object. | ||
* `user` - The user class (not instance!) being used by the application. | ||
|
||
## How to use it | ||
|
||
There are different ways of calling this middleware in any Esmerald application. | ||
|
||
### Via settings | ||
|
||
```python | ||
{!> ../docs_src/configurations/jwt/settings.py!} | ||
``` | ||
|
||
### Via application instantiation | ||
|
||
```python | ||
{!> ../docs_src/databases/edgy/middleware/example1.py !} | ||
``` | ||
|
||
### Via overriding the JWTAuthMiddleware | ||
|
||
=== "Via app instance" | ||
|
||
```python | ||
{!> ../docs_src/databases/edgy/middleware/example2.py !} | ||
``` | ||
|
||
=== "Via app settings" | ||
|
||
```python | ||
{!> ../docs_src/databases/edgy/middleware/example3.py !} | ||
``` | ||
|
||
### Important note | ||
|
||
In the examples you could see sometimes the `StarletteMiddleware` being used and in other you didn't. The reason behind | ||
is very simple and also explained in the [middleware section](../../middleware/middleware.md#important). | ||
|
||
If you need to specify parameters in your middleware then you will need to wrap it in a `starlette.middleware.Middleware` | ||
object to do it so. | ||
|
||
If no parameters are needed, then you can simply pass the middleware class directly and Esmerald will take care of the | ||
rest. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
# Models | ||
|
||
In simple terms, models are a representation of a database table in the format of an object declared by the language | ||
implementing. | ||
|
||
## User models | ||
|
||
Integrating with Edgy, Esmerald already provides some of the models that helps you with the | ||
initial configuration. | ||
|
||
1. `AbstractUser` - The base user class containing all the fields required a user. | ||
2. `User` - A subsclass of `AbstractUser` | ||
|
||
## User | ||
|
||
Extenting the existing `User` model is as simple as this: | ||
|
||
```python hl_lines="17 32" | ||
{!> ../docs_src/databases/edgy/models.py !} | ||
``` | ||
|
||
This is a clean way of declaring the models and using the Edgy docs, you can easily understand | ||
why this is like the way it is. | ||
|
||
### Meta class | ||
|
||
There are wayf of making the models and the registry cleaner, after all, you might want to use the | ||
same registry in different models across multiple applications in your codebase. | ||
|
||
One way and a way Esmerald always recommend, is by leveraging the [settings](../../application/settings.md) | ||
|
||
### Leveraging the settings for your models | ||
|
||
Let us use the same example but this time, we will be using the settings. | ||
Since **you can access the settings anywhere in the codebase**. | ||
|
||
Check it out the example below and how by using the settings, you can literally leverage Esmerald | ||
with Edgy. | ||
|
||
=== "settings.py" | ||
|
||
```python hl_lines="10-12" | ||
{!> ../docs_src/databases/edgy/settings/settings.py !} | ||
``` | ||
|
||
=== "models.py" | ||
|
||
```python hl_lines="17 32" | ||
{!> ../docs_src/databases/edgy/settings/models.py !} | ||
``` | ||
|
||
You simply isolated your common database connection and registry inside the globally accessible | ||
settings and with that you can import in any Esmerald application, ChildEsmerald or whatever you | ||
prefer without the need of repeating yourself. | ||
|
||
### User model fields | ||
|
||
If you are familiar with Django then you are also aware of the way they have their users table and the way they | ||
have the fields declared. Esmerald has a similar approach and provides the following. | ||
|
||
* `first_name` | ||
* `last_name` | ||
* `username` | ||
* `email` | ||
* `password` | ||
* `last_login` | ||
* `is_active` | ||
* `is_staff` | ||
* `is_superuser` | ||
|
||
### The functions available | ||
|
||
Using simply this model it does not bring too much benefits as it si something you can do easily and fast but the | ||
functionality applied to this model is already something that can be something that requires some extra time to | ||
assemble. | ||
|
||
!!! Warning | ||
The following examples assume that you are taking advantage of the settings as | ||
[decribed before](#leveraging-the-settings-for-your-models). | ||
|
||
**create_user** | ||
|
||
```python | ||
{!> ../docs_src/databases/edgy/create_user.py !} | ||
``` | ||
|
||
**create_superuser** | ||
|
||
```python | ||
{!> ../docs_src/databases/edgy/create_superuser.py !} | ||
``` | ||
|
||
**check_password** | ||
|
||
```python hl_lines="28" | ||
{!> ../docs_src/databases/edgy/check_password.py !} | ||
``` | ||
|
||
Because you are using the `User` provided by Esmerald, the same object is also prepared to validate | ||
the password against the system. If you are familiar with Django, this was based on it and has the | ||
same principle. | ||
|
||
**set_password** | ||
|
||
```python hl_lines="28" | ||
{!> ../docs_src/databases/edgy/set_password.py !} | ||
``` | ||
|
||
The same for setting passwords. The `User` already contains the functionality to set a password of | ||
a given `User` instance. | ||
|
||
### What happened | ||
|
||
Although the way of using the `User` table was intentionally designed to be simple there is in fact a lot going | ||
on behind the scenes. | ||
|
||
When using the `create_user` and `create_superuser` behind the scenes it is not only creating that same record and | ||
storing in the database but is also <a href='https://nordpass.com/blog/password-hash/' target='_blank'>hashing</a> | ||
the password for you using the built-in Esmerald [password hashers](#password-hashers) and this is a life saving | ||
time and implementation. | ||
|
||
Esmerald also provides the `set_password` and `check_password` functions to make it easier to | ||
validate and change a user's password using the `User` instance. | ||
|
||
## Password Hashers | ||
|
||
Esmerald already brings some pre-defined password hashers that are available in the | ||
[Esmerald settings](../../application/settings.md) and ready to be used. | ||
|
||
```python | ||
|
||
@property | ||
def password_hashers(self) -> List[str]: | ||
return [ | ||
"esmerald.contrib.auth.hashers.PBKDF2PasswordHasher", | ||
"esmerald.contrib.auth.hashers.PBKDF2SHA1PasswordHasher", | ||
] | ||
|
||
``` | ||
|
||
Esmerald uses <a href='https://passlib.readthedocs.io/en/stable/' target='_blank'>passlib</a> under the hood | ||
in order to facilitate the process of hashing passwords. | ||
|
||
You can always override the property `password_hashers` in your | ||
[custom settings](../../application/settings.md#custom-settings) and use your own. | ||
|
||
```python | ||
{!> ../docs_src/databases/edgy/hashers.py !} | ||
``` | ||
|
||
## Migrations | ||
|
||
You can use any migration tool as you see fit. It is recommended | ||
<a href='https://alembic.sqlalchemy.org/en/latest/' target='_blank'>Alembic</a>. | ||
|
||
Edgy also provides some insights in | ||
[how to migrate using alembic](https://edgy.tarsild.io/migrations/migrations). | ||
|
||
## General example | ||
|
||
More examples and more thorough explanations how to use [Edgy](https://edgy.tarsild.io) | ||
can be consulted in their [documentation](https://edgy.tarsild.io). |
Oops, something went wrong.