Repository: vitalik/django-ninja
Pull request #1409 (2025-02-11 03:28:44)

Add method to check is user either staff or superuser.
Add method to check is user either staff or superuser.

adambirds: Would be gerat if this method could be added @vitalik, let me know if it needs any amendments.
vitalik: I think the check of is_superuser (and naming it so) is obsolete

in django world if user is superuser that it's automatically is staff and has all the permissions


https://github.com/django/django/blob/41239fe34d64e801212dccaa4585e4802d0fac68/django/contrib/auth/models.py#L384-L397


I would just call it IsStaff : 


```Python
class SessionAuthSuperUser(APIKeyCookie):
    "Reusing Django session authentication & verify that the user is a super user"

    param_name: str = settings.SESSION_COOKIE_NAME

    def authenticate(self, request: HttpRequest, key: Optional[str]) -> Optional[Any]:
        is_superuser = getattr(request.user, "is_superuser", None)
        if request.user.is_authenticated and is_superuser:
            return request.user

        return None


class SessionAuthSuperUserOrStaff(SessionAuthSuperUser):
    
def authenticate(self, request: HttpRequest, key: Optional[str]) -> Optional[Any]:
        result = super().authenticate(request, key)
        if result:
              return result
        is_staff = getattr(request.user, "is_sstaff", None)
        if request.user.is_authenticated and (is_superuser or is_staff):
            return request.user

        return None
```

Patch:
diff --git a/ninja/security/__init__.py b/ninja/security/__init__.py
index af1cf6d..ec3672d 100644
--- a/ninja/security/__init__.py
+++ b/ninja/security/__init__.py
@@ -1,6 +1,6 @@
 from ninja.security.apikey import APIKeyCookie, APIKeyHeader, APIKeyQuery
 from ninja.security.http import HttpBasicAuth, HttpBearer
-from ninja.security.session import SessionAuth, SessionAuthSuperUser
+from ninja.security.session import SessionAuth, SessionAuthSuperUser, SessionAuthIsStaff
 
 __all__ = [
     "APIKeyCookie",
@@ -12,7 +12,9 @@ __all__ = [
     "SessionAuthSuperUser",
     "django_auth",
     "django_auth_superuser",
+    "django_auth_is_staff",
 ]
 
 django_auth = SessionAuth()
 django_auth_superuser = SessionAuthSuperUser()
+django_auth_is_staff = SessionAuthIsStaff()
\ No newline at end of file
diff --git a/ninja/security/session.py b/ninja/security/session.py
index 62ab7cd..88e5822 100644
--- a/ninja/security/session.py
+++ b/ninja/security/session.py
@@ -5,7 +5,7 @@ from django.http import HttpRequest
 
 from ninja.security.apikey import APIKeyCookie
 
-__all__ = ["SessionAuth", "SessionAuthSuperUser"]
+__all__ = ["SessionAuth", "SessionAuthSuperUser", "SessionAuthIsStaff"]
 
 
 class SessionAuth(APIKeyCookie):
@@ -31,3 +31,15 @@ class SessionAuthSuperUser(APIKeyCookie):
             return request.user
 
         return None
+
+
+class SessionAuthIsStaff(SessionAuthSuperUser):
+    
+    def authenticate(self, request: HttpRequest, key: Optional[str]) -> Optional[Any]:
+        result = super().authenticate(request, key)
+        if result:
+              return result
+        if getattr(request.user, "is_staff", None):
+            return request.user
+
+        return None


Test patch:
diff --git a/tests/test_auth.py b/tests/test_auth.py
index 01fb648..d0db30a 100644
--- a/tests/test_auth.py
+++ b/tests/test_auth.py
@@ -12,6 +12,7 @@ from ninja.security import (
     HttpBearer,
     django_auth,
     django_auth_superuser,
+    django_auth_is_staff,
 )
 from ninja.security.base import AuthBase
 from ninja.testing import TestClient
@@ -79,6 +80,7 @@ def on_custom_error(request, exc):
 for path, auth in [
     ("django_auth", django_auth),
     ("django_auth_superuser", django_auth_superuser),
+    ("django_auth_is_staff", django_auth_is_staff),
     ("callable", callable_auth),
     ("apikeyquery", KeyQuery()),
     ("apikeyheader", KeyHeader()),
@@ -102,6 +104,10 @@ class MockSuperUser(str):
     is_authenticated = True
     is_superuser = True
 
+class MockStaffUser(str):
+    is_authenticated = True
+    is_staff = True
+
 
 BODY_UNAUTHORIZED_DEFAULT = dict(detail="Unauthorized")
 BODY_FORBIDDEN_DEFAULT = dict(detail="Forbidden")
@@ -125,6 +131,25 @@ BODY_FORBIDDEN_DEFAULT = dict(detail="Forbidden")
             200,
             dict(auth="admin"),
         ),
+        ("/django_auth_is_staff", {}, 401, BODY_UNAUTHORIZED_DEFAULT),
+        (
+            "/django_auth_is_staff",
+            dict(user=MockUser("admin")),
+            401,
+            BODY_UNAUTHORIZED_DEFAULT,
+        ),
+        (
+            "/django_auth_is_staff",
+            dict(user=MockSuperUser("admin")),
+            200,
+            dict(auth="admin"),
+        ),
+        (
+            "/django_auth_is_staff",
+            dict(user=MockStaffUser("admin")),
+            200,
+            dict(auth="admin"),
+        ),
         ("/callable", {}, 401, BODY_UNAUTHORIZED_DEFAULT),
         ("/callable?auth=demo", {}, 200, dict(auth="demo")),
         ("/apikeyquery", {}, 401, BODY_UNAUTHORIZED_DEFAULT),
@@ -221,6 +246,7 @@ def test_schema():
         "KeyQuery": {"in": "query", "name": "key", "type": "apiKey"},
         "SessionAuth": {"in": "cookie", "name": "sessionid", "type": "apiKey"},
         "SessionAuthSuperUser": {"in": "cookie", "name": "sessionid", "type": "apiKey"},
+        "SessionAuthIsStaff": {"in": "cookie", "name": "sessionid", "type": "apiKey"},
     }
     # TODO: Samename for schema check
     # TODO: check operation security attributes