Permissions¶
Authentication and authorization are a must in every application. Managing those via dependencies is extremely possible and also widely used but with Esmerald you have a clear separation of permissions although still allowing inject to happen as well.
Inspired by the same author of Django Rest Framework, Esmerald permissions are as simple as you want them to be and as complex as you design. For all tastes.
Note
This permission system is not the same as the Lilya permission system.
BasePermission and custom permissions¶
If you are used to Django Rest Framework, the way Esmerald designed was very much within the same lines and with all the huge community of developer in mind to make a transition almost immediate and simple as possible.
All the permission classes must derive from BasePermission
.
The permissions must implement the has_permission
and return True
or False
.
The permissions can also be async
in case you need to run awaitables.
An example of a permission for an user admin.
from esmerald import Request
from esmerald.permissions.base import BaseAbstractUserPermission
from esmerald.types import APIGateHandler
class IsUserAdmin(BaseAbstractUserPermission):
"""
Simply check if a user has admin access or not.
BaseAbstractUserPermission inherits from BasePermission.
"""
def is_user_authenticated(self, request: "Request") -> bool:
"""
Logic to check if the user is authenticated
"""
...
def is_user_staff(self, request: "Request") -> bool:
"""
Logic to check if user is staff
"""
...
def has_permission(self, request: "Request", apiview: "APIGateHandler"):
super().has_permission(request, apiview)
return bool(request.user and self.is_user_staff(request))
An example of a permission for an user admin with async.
from esmerald import Request
from esmerald.permissions.base import BaseAbstractUserPermission
from esmerald.types import APIGateHandler
class IsUserAdmin(BaseAbstractUserPermission):
"""
Simply check if a user has admin access or not.
BaseAbstractUserPermission inherits from BasePermission.
"""
def is_user_authenticated(self, request: "Request") -> bool:
"""
Logic to check if the user is authenticated
"""
...
def is_user_staff(self, request: "Request") -> bool:
"""
Logic to check if user is staff
"""
...
async def has_permission(self, request: "Request", apiview: "APIGateHandler"):
super().has_permission(request, apiview)
return bool(request.user and self.is_user_staff(request))
An example of a permission for a project
from esmerald import BasePermission, Request
from esmerald.types import APIGateHandler
class IsProjectAllowed(BasePermission):
"""
Permission to validate if has access to a given project
"""
def has_permission(self, request: "Request", apiview: "APIGateHandler"):
allow_project = request.headers.get("allow_access")
return bool(allow_project)
An example of a permission for a project with async
from esmerald import BasePermission, Request
from esmerald.types import APIGateHandler
class IsProjectAllowed(BasePermission):
"""
Permission to validate if has access to a given project
"""
async def has_permission(self, request: "Request", apiview: "APIGateHandler"):
allow_project = request.headers.get("allow_access")
return bool(allow_project)
Esmerald and permissions¶
Esmerald giving support to Saffier ORM and Edgy also provides some default permissions that can be linked to the models also provided by Esmerald.
IsAdminUser and example of provided permissions¶
This is a simple permission that extends the BaseAbstractUserPermission
and checks if a user is authenticated or not.
The functionality of verifying if a user might be or not authenticated was separated from the
Saffier and instead you must implement the is_user_authenticated()
function when inheriting from BaseAbstractUserPermission
or IsAdminUser
.
Esmerald and provided permissions¶
Esmerald provides some default permissions that can be used in your projects but not too many as every project has special needs and rules.
- DenyAll - As the name suggests, blocks access to anyone. Can be useful if an API is under maintenance.
- AllowAny - The opposite of DenyAll. Allows access to everyone. Useful as permission for the top Esmerald application.
- IsAdminUser - Checks if a user is admin or not by inheriting the object and implementing
is_user_staff
function. - IsAuthenticated - Checks if a user is authenticated by inheriting from the object and implementing the logic
for
is_user_authenticated
function. - IsAuthenticatedOrReadOnly - Checks if a user is authenticated or a read only request. For the autenticated
part the
is_user_authenticated
needs to be implemented.
How to use¶
To use the IsAdminUser
, IsAuthenticated
and IsAuthenticatedOrReadOnly
is as simple as the example below.
from esmerald import APIView, Esmerald, Gateway, JSONResponse, Request, get
from esmerald.permissions import AllowAny, IsAdminUser, IsAuthenticated, IsAuthenticatedOrReadOnly
class IsAdmin(IsAdminUser):
def is_user_staff(self, request: "Request") -> bool:
"""
Add logic to verify if a user is staff
"""
class IsUserAuthenticated(IsAuthenticated):
def is_user_authenticated(self, request: "Request") -> bool:
"""
Add logic to verify if a user is staff
"""
class IsAuthOrReadOnly(IsAuthenticatedOrReadOnly):
def is_user_authenticated(self, request: "Request") -> bool:
"""
Add logic to verify if a user is staff
"""
@get()
async def home() -> None: ...
class UserAPIView(APIView):
path = "/users"
permissions = [IsUserAuthenticated]
@get("/admin", permissions=[IsAdmin])
async def admin(self, request: Request) -> JSONResponse:
return JSONResponse({"message": "ok"})
app = Esmerald(
routes=[Gateway("/home", handler=home), Gateway(handler=UserAPIView)],
permissions=[AllowAny],
)
- The main app
Esmerald
has anAllowAny
permission for the top level - The
UserAPIView
object has aIsUserAuthenticated
allowing only authenticated users to access any of the endpoints under the class (endpoints under/users
). - The
/users/admin
has a permissionIsAdmin
allowing only admin users to access the specific endpoint
More on permissions¶
The permissions internally are checked from top down which means you can place permissios at any given part of the level making it more dynamic and allowing to narrow it down to a granular level that is managenable.
Internally, Esmerald runs all the validations and checks and on an application level, the only
thing you need to make sure is to implement the has_permission
on any derived class of
BasePermission.
Permissions summary¶
- All permissions must inherit from
BasePermission
. BasePermission
has thehas_permission(request Request, apiview: "APIGateHandler") and it can be
async` or not.- The handlers, Gateway, WebSocketGateway, Include and Esmerald can have as many permissions as you want.
API Reference¶
Check out the API Reference for Permissions for more details.