Skip to content

Authentication

Like SQLAdmin, Esmerald Admin does not enfoce any authentication, but provides two out-of-the-box optional EmailAdminAuth and UsernameAdminAuth using the Esmerald contrib auth for Saffier that you can use or models for Edgy.

Note

If you don't use the Esmerald models for Saffier or Edgy, you can simply ignore this and use your own implementation and follow the instructions from the SQLAdmin.

EmailAdminAuth and UsernameAdminAuth

These two authentication backends use the AuthenticationBackend from SQLAdmin and apply logins using email or username, your choice.

EmailAdminAuth and UsernameAdminAuth expect three parameters:

  • secret_key - The secret to be used with your auth.
  • auth_model - The class object of your User, usually derived from the User model for Saffier or User model for Edgy.
  • config - The application settings object. It can be the settings config or the application settings from esmerald.conf. It all depends of which one you use.

How to use

This is how you could use the backends in your Esmerald application. This example is very simple and it will use the settings_config from Esmerald to simplify. This is not mandatory and you can use your preferred way.

from esmerald import Esmerald, EsmeraldAPISettings, Include
from esmerald.config.jwt import JWTConfig
from esmerald.contrib.auth.saffier.base_user import AbstractUser
from saffier import Database, Registry

from esmerald_admin import Admin
from esmerald_admin.backends.saffier.email import EmailAdminAuth

database = Database("sqlite:///db.sqlite")
registry = Registry(database=database)


class AppSettings(EsmeraldAPISettings):
    @property
    def jwt_config(self) -> JWTConfig:
        return JWTConfig(signing_key=self.secret_key)


class User(AbstractUser):
    """Inherits from the user base"""

    class Meta:
        registry = registry


# You can use the `settings_config` directly or ESMERALD_SETTINGS_MODULE
settings = AppSettings()


def get_application():
    """
    This is optional. The function is only used for organisation purposes.
    """

    app = Esmerald(
        routes=[Include(namespace="linezap.urls")],
        on_startup=[database.connect],
        on_shutdown=[database.disconnect],
        settings_config=settings,
    )

    # EmailAdminAuth or UsernameAdminAuth
    auth_backend = EmailAdminAuth(
        secret_key=settings.secret_key, auth_model=User, config=settings.jwt_config
    )
    Admin(app, registry.engine, authentication_backend=auth_backend)

    return app


app = get_application()
from edgy import Database, Registry
from esmerald import Esmerald, EsmeraldAPISettings, Include
from esmerald.config.jwt import JWTConfig
from esmerald.contrib.auth.edgy.base_user import AbstractUser

from esmerald_admin import Admin
from esmerald_admin.backends.edgy.email import EmailAdminAuth

database = Database("sqlite:///db.sqlite")
registry = Registry(database=database)


class AppSettings(EsmeraldAPISettings):
    @property
    def jwt_config(self) -> JWTConfig:
        return JWTConfig(signing_key=self.secret_key)


class User(AbstractUser):
    """Inherits from the user base"""

    class Meta:
        registry = registry


# You can use the `settings_config` directly or ESMERALD_SETTINGS_MODULE
settings = AppSettings()


def get_application():
    """
    This is optional. The function is only used for organisation purposes.
    """

    app = Esmerald(
        routes=[Include(namespace="linezap.urls")],
        on_startup=[database.connect],
        on_shutdown=[database.disconnect],
        settings_config=settings,
    )

    # EmailAdminAuth or UsernameAdminAuth
    auth_backend = EmailAdminAuth(
        secret_key=settings.secret_key, auth_model=User, config=settings.jwt_config
    )
    Admin(app, registry.engine, authentication_backend=auth_backend)

    return app


app = get_application()

To use the UsernameAdminAuth instead:

from esmerald_admin.backends.saffier.username import UsernameAdminAuth
from esmerald_admin.backends.edgy.username import UsernameAdminAuth

The User model as you can notice, is the one that derived from Esmerald and the reason for this is because the Esmerald models come with a lot of functionality already built-in such as password hashing and checks for passwords and therefore, makes it easier to use it.

Tip

SQLAdmin has clear instructions in case you want to build your own backend authentication from the scratch.

Using OAuth

This implementation can be found in the official SQLAdmin side.

Permissions

Esmerald Admin uses the same base as SQLAdmin but adds some extra uniquenesses to it such as the pattern for the permissions.

In Esmerald, the permissions are extremely powerfull and yet simple to implement. Esmerald admin opted to follow the same pattern.

What is is_accessible in SQLAdmin, in Esmerald admin is called has_permission. This way the consistency is maintained and the common systax preserved.

The ModelView and the BaseView classes in Esmerald Admin also implement two methods you can override. These methods are used for the control of each Model/View in addition to the AuthenticationBackend.

  • is_visible - Controls if the Model/View should be displayed in the menu or not.
  • has_permission - Controls if the Model/View should be accessed.

Like SQLAdmin, both methods implement the same signature and should return a bool.

from esmerald import Request
from esmerald.contrib.auth.saffier.base_user import AbstractUser
from saffier import Database, Registry

from esmerald_admin import ModelView

database = Database("sqlite:///db.sqlite")
registry = Registry(database=database)


class User(AbstractUser):
    """Inherits from the user base"""

    class Meta:
        registry = registry


# Use the declarative from Saffier
UserDeclarative = User.declarative()


class UserAdmin(ModelView, model=UserDeclarative):
    def is_visible(self, request: Request) -> bool:
        # Check incoming request
        # For example request.session if using AuthenticationBackend
        return True

    def has_permission(self, request: Request) -> bool:
        # Check incoming request
        # For example request.session if using AuthenticationBackend
        return True
from edgy import Database, Registry
from esmerald import Request
from esmerald.contrib.auth.edgy.base_user import AbstractUser

from esmerald_admin import ModelView

database = Database("sqlite:///db.sqlite")
registry = Registry(database=database)


class User(AbstractUser):
    """Inherits from the user base"""

    class Meta:
        registry = registry


# Use the declarative from Saffier
UserDeclarative = User.declarative()


class UserAdmin(ModelView, model=UserDeclarative):
    def is_visible(self, request: Request) -> bool:
        # Check incoming request
        # For example request.session if using AuthenticationBackend
        return True

    def has_permission(self, request: Request) -> bool:
        # Check incoming request
        # For example request.session if using AuthenticationBackend
        return True