#36837: Client.force_login won't work for permission-only backends inheriting 
from
BaseBackend
-------------------------------------+-------------------------------------
     Reporter:  Christian Hartung    |                     Type:
                                     |  Cleanup/optimization
       Status:  new                  |                Component:
                                     |  contrib.auth
      Version:  6.0                  |                 Severity:  Normal
     Keywords:                       |             Triage Stage:
                                     |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
 This is a follow up to #27542.

 Take the `MagicAdminBackend` from the
 [https://docs.djangoproject.com/en/6.0/topics/auth/customizing/#handling-
 authorization-in-custom-backends documentation]. It is an Authentication
 Backend that inherits from `BaseBackend` and overrides the `has_perm`
 method. This leaves it with the default `get_user` method returning
 `None`, making it eligible for `Client.force_login`.

 Some libraries like `django-rules` and `django-guardian` work around this
 by not inheriting from `BaseBackend`, and not adding a `get_user` method,
 but:
 * this goes against the documentation, which states that both `get_user`
 and `authenticate` are required, and permission related methods are
 optional
 * now they don't automatically gain the `async` functionality, making it
 harder for users to integrate those libraries in an `async` context

 I don't know a good way around this. Some ideas are:

 **Add some introspection capabilities to BaseBackend**
 I was think something like:
 {{{
 class BaseBackend:
     supports_authentication = True
     supports_permissions = True

 class ObjectPermissionBackend(BaseBackend):
     supports_authentication = False
     supports_permissions = True
 }}}

 This way we could change `(a)authenticate` and `force_login` to skip
 backends that do not support autentication, and `(a)has_perm` to skip
 backends that do not support permissions.

 {{{
 def _get_compatible_backends(request, **credentials):
     for backend, backend_path in _get_backends(return_tuples=True):
         if not getattr(backend, "supports_authentication", True):
             continue

         ... Check signature and so on

 def _user_has_perm(user, perm, obj):
     for backend in auth.get_backends():
         if not getattr(backend, "supports_permissions", True):
             continue

         if not hasattr(backend, "has_perm"):
             continue

         ... Check permission
 }}}

 **Call `get_user` inside `force_login`**
 Use the first backend that returns something. This was suggested on
 #27542, but it could be really slow and might introduce some side effects

 **Updating the documentation**
 Simply update the documentation stating that:
 * `get_user` required only if the backend can authenticate users
 (`authenticate` returns something)
 * `Client.force_login` selects the first backend that has a `get_user`.
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36837>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/0107019b70c5ce8d-330af997-9352-4032-a31d-54745632dfcf-000000%40eu-central-1.amazonses.com.

Reply via email to