Permissions

FastAPI Mongo Admin provides Django-style permission hooks on ModelAdmin. Each hook returns a boolean indicating whether the current user may perform the action.

Permission hooks

Method

Guards

has_view_permission(request, user, obj=None)

Changelist access

has_add_permission(request, user)

Add form

has_change_permission(request, user, obj=None)

Change form, bulk actions

has_delete_permission(request, user, obj=None)

Delete confirmation, bulk delete

Default behavior: all hooks return True.

Basic example

class ProductAdmin(ModelAdmin):
    def has_add_permission(self, request, user=None) -> bool:
        return bool(user and user.get("is_staff"))

    def has_change_permission(self, request, user=None, obj=None) -> bool:
        return bool(user and user.get("role") in ("admin", "manager"))

    def has_delete_permission(self, request, user=None, obj=None) -> bool:
        return bool(user and user.get("role") == "admin")

Role-based access

class ProductAdmin(ModelAdmin):
    def has_delete_permission(self, request, user=None, obj=None) -> bool:
        return bool(user and user.get("role") in ("admin", "manager"))

In the ecommerce demo, viewers can browse products but cannot delete them.

Object-level permissions

Use the obj parameter for per-document rules:

def has_change_permission(self, request, user=None, obj=None) -> bool:
    if not user:
        return False
    if user.get("role") == "admin":
        return True
    if obj and obj.get("owner_id") == user.get("id"):
        return True
    return False

Bulk action permissions

Bulk actions (except delete_selected) require has_change_permission.

The built-in delete_selected action checks has_delete_permission before deleting documents.

Action-level permissions

The @action decorator accepts an optional permissions list for future fine-grained control:

@action("Publish selected", permissions=["can_publish"])
async def publish_products(self, request, queryset):
    pass

Permission denied responses

When a permission check fails, the admin raises PermissionDeniedError (HTTP 403). Unauthenticated users receive 401 from your auth_dependency.

How checks are wired

The router creates per-action dependencies via require_permission():

GET  /admin/{collection}/           → has_view_permission
GET  /admin/{collection}/add/       → has_add_permission
POST /admin/{collection}/add/       → has_add_permission
GET  /admin/{collection}/{id}/change/ → has_change_permission
POST /admin/{collection}/action/    → has_change_permission (bulk)
GET  /admin/{collection}/{id}/delete/ → has_delete_permission

The authenticated user object from your auth_dependency is passed to each hook.