ferrosoft.apps.ferrobase.middleware package

Submodules

ferrosoft.apps.ferrobase.middleware.appmeta module

class ferrosoft.apps.ferrobase.middleware.appmeta.AppMetaMiddleware(get_response)[source]

Bases: object

Set APP_META dict in request object.

This dict includes information about the application: NAME and ICON_PATH. The app is determined by HTTP Host header, allowing different branding per app.

HOST_NAME_MAX_LENGTH = 255
SETTINGS_KEY = 'FERROBASE_APP_HOSTS'
async_capable = True
sync_capable = False

ferrosoft.apps.ferrobase.middleware.asgi module

class ferrosoft.apps.ferrobase.middleware.asgi.TenantASGIMiddleware(inner)[source]

Bases: BaseMiddleware

This middleware is like TenantMiddleware but for ASGI applications.

Only query parameters and cookies are inspected for tenant ID.

Well, not exactly the same thing. The resolved tenant is stored in scope field “tenant”. The thread-local variable is not written, because it cannot be guaranteed that the ASGI consumer is actually in the same thread. Thus, consumers need to explicitly use the context manager activate_tenant_database.

ferrosoft.apps.ferrobase.middleware.cache module

class ferrosoft.apps.ferrobase.middleware.cache.CacheControlMiddleware(get_response)[source]

Bases: object

Middleware that ensures every response carries a Cache-Control header.

If the upstream view already set the header, it is left untouched. Otherwise the value is taken from settings.CACHE_CONTROL, falling back to "no-store" so sensitive platform responses are never cached by default.

async_capable = True
sync_capable = False

ferrosoft.apps.ferrobase.middleware.language module

class ferrosoft.apps.ferrobase.middleware.language.UserLanguageMiddleware(get_response)[source]

Bases: object

Middleware that activates the authenticated user’s preferred language for each request.

Reads user.language.iso_code and calls django.utils.translation.activate so that all subsequent gettext calls in the request–response cycle use the user’s chosen locale. Unauthenticated or language-less users receive Django’s default language.

async_capable = True
sync_capable = True

ferrosoft.apps.ferrobase.middleware.tenant module

exception ferrosoft.apps.ferrobase.middleware.tenant.BaseError[source]

Bases: Exception

Base class for all tenant-resolution errors.

class ferrosoft.apps.ferrobase.middleware.tenant.GlobalRouter[source]

Bases: object

Send queries for Django’s built-in apps and some of Ferrobase to default database.

db_for_read(model, **hints)[source]
db_for_write(model, **hints)[source]
exception ferrosoft.apps.ferrobase.middleware.tenant.TenantChoiceRequired[source]

Bases: BaseError

Raised when no tenant has been chosen and user interaction is required to select one.

class ferrosoft.apps.ferrobase.middleware.tenant.TenantMiddleware(get_response)[source]

Bases: object

ASGI-capable middleware that resolves and persists the active tenant per request.

Reads the tenant ID from POST → GET → cookie (in that order of priority). When the ID arrives via POST or GET, it is written back to the cookie so subsequent requests do not need to repeat it. The resolved Tenant instance is stored on request.current_tenant and in the active_tenant context variable so ORM queries are automatically routed to the correct database.

Unauthenticated requests and the tenant-selection view itself bypass resolution to avoid redirect loops.

TENANT_REQUEST_KEY = 'fptid'

Name of the cookie and GET/POST parameter that carries the tenant ID.

async classmethod adetermine_tenant(request, choose_url)[source]
async_capable = True
static choose_url(request)[source]
sync_capable = False
exception ferrosoft.apps.ferrobase.middleware.tenant.TenantNotAllowed[source]

Bases: BaseError

Raised when the authenticated user is not authorised to access the requested tenant.

exception ferrosoft.apps.ferrobase.middleware.tenant.TenantNotFound[source]

Bases: BaseError

Raised when the requested tenant ID does not exist or is inactive.

class ferrosoft.apps.ferrobase.middleware.tenant.TenantRouter[source]

Bases: object

Send queries to tenant database.

If no tenant is available, None is returned. TenantMiddleware makes sure to plug all holes so that no user can accidentally write Ferrosoft Platform objects to the default database.

db_for_read(model, **hints)[source]
db_for_write(model, **hints)[source]
async ferrosoft.apps.ferrobase.middleware.tenant.aget_validated_tenant(tenant_id, user)[source]

Async variant of get_validated_tenant.

Parameters:
  • tenant_id (str) – Primary key string of the tenant to resolve.

  • user (User | None) – Authenticated user, or None to skip authorisation.

Returns:

The validated, active Tenant instance.

Return type:

Tenant

Raises:
async ferrosoft.apps.ferrobase.middleware.tenant.avalidate_chosen_tenant(tenant_id, user, organization)[source]

Async variant of validate_chosen_tenant.

Parameters:
  • tenant_id (str | None) – Tenant primary key string, or None.

  • user (User | None) – Currently authenticated user for authorisation checks.

  • organization (Organization) – The user’s organisation.

Returns:

The resolved Tenant instance.

Return type:

Tenant

Raises:

TenantChoiceRequired – When the tenant cannot be resolved automatically.

ferrosoft.apps.ferrobase.middleware.tenant.get_validated_tenant(tenant_id, user)[source]

Look up and authorise a tenant synchronously.

Parameters:
  • tenant_id (str) – Primary key string of the tenant to resolve (must be a valid UUID; malformed values are treated as not-found).

  • user (User | None) – Authenticated user to check against the tenant’s organisation, or None to skip the authorisation check.

Returns:

The validated, active Tenant instance.

Return type:

Tenant

Raises:
ferrosoft.apps.ferrobase.middleware.tenant.validate_chosen_tenant(tenant_id, user, organization)[source]

Resolve the tenant to use for an organisation, redirecting when no choice is made.

If tenant_id is provided, it is validated; otherwise the organisation’s single tenant is used automatically. When the organisation has multiple tenants and none is specified, or when the requested tenant is invalid, TenantChoiceRequired is raised so the caller can redirect to the selection page.

Parameters:
  • tenant_id (str | None) – Tenant primary key string, or None.

  • user (User | None) – Currently authenticated user for authorisation checks.

  • organization (Organization) – The user’s organisation used to enumerate available tenants.

Returns:

The resolved Tenant instance.

Return type:

Tenant

Raises:

TenantChoiceRequired – When the tenant cannot be resolved automatically.