2

I'm working on an application with a DRF base, requiring LDAP authentication. I have run across a problem, by which I am seeing different behaviours for users logging in directly via the DRF API and those accessing it from an external application - ie Curl/Postman.

In the application, once logged in I present a series of views/urls that users can access, ie the url below to view 'samples':

http://:8000/api/samples

I have applied DjangoModelPermissions using a custom permission class to my Sample view which requires model permission to view the model, ie:

class HasModelPermission(permissions.DjangoModelPermissions):
    perms_map = {
        'GET': ['%(app_label)s.read_%(model_name)s'],
        'OPTIONS': ['%(app_label)s.read_%(model_name)s'],
        'HEAD': ['%(app_label)s.read_%(model_name)s'],
        'POST': ['%(app_label)s.add_%(model_name)s'],
        'PUT': ['%(app_label)s.change_%(model_name)s'],
        'PATCH': ['%(app_label)s.change_%(model_name)s'],
        'DELETE': ['%(app_label)s.delete_%(model_name)s'],
    }

class SampleView(generics.ListAPIView):
    queryset = Sample.objects.all()
    serializer_class = SampleSerializer
    permission_classes = [HasModelPermission]

As per https://django-auth-ldap.readthedocs.io/en/latest/permissions.html#group-mirroring, I've configured my ldap setup with AUTH_LDAP_FIND_GROUP_PERMS=true and have created matching DJANGO groups for LDAP groups I care about - and I have allocated model permissions to these groups. This is working as I'd expect - only users who are members of groups with permission to view the list are able to see it.

The key issue I am seeing is that while the internal DRF API is correctly determining user permissions, users logging in externally and then submitting GET requests are determined NOT to have permission to view.

I have identified via some debugging that the cause is a failed test in the following code from django_auth_ldap.backend.py which gets called from the view dispatcher in DRF, essentially, when I access the url from built in the DRF API, "hasattr(user, 'ldap_user'):" returns true, when I connect via postman or curl etc, "hasattr(user, 'ldap_user'):" returns false - obviously meaning no user permissions are determined to exist.

def get_group_permissions(self, user, obj=None):
    if not hasattr(user, 'ldap_user') and self.settings.AUTHORIZE_ALL_USERS:
        _LDAPUser(self, user=user)  # This sets user.ldap_user

    if hasattr(user, 'ldap_user'):
        permissions = user.ldap_user.get_group_permissions()
    else:
        permissions = set()

    return permissions

So I guess ultimately my question is, how should I be populating/maintaining the user.ldap_user attribute between HTTP operations from remote endpoints. (note I am using session and token authentication ATM). I'm guessing my issue is more to do with either maintaining a users lda_user attribute data between operations, or being able to retrieve it on each successive operation.

Matt27
  • 29
  • 3

2 Answers2

0

I had the same error and fixed it with django_auth_ldap LDAPackend.

I also had my own Token model; here's the shorted version:

from django_auth_ldap.backend import LDAPBackend
from rest_framework import authentication
from users.models import Token

class TokenAuthentication(authentication.TokenAuthentication):
    def authenticate_credentials(self, key):
        token = Token.objects.prefetch_related('user').get(key=key)
       
        ldap_backend = LDAPBackend()
        user = ldap_backend.populate_user(token.user.username)
        if user:
            return user, token

        return token.user, token
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
robort
  • 1
0

By setting AUTH_LDAP_AUTHORIZE_ALL_USERS = True you'll authenticate all requests (also the curl/postman ones).

If you only want it for your token-auth endpoint (less overhead than populate_user()) you could go for:

class TokenAuthentication(TokenAuthentication):
    def authenticate(self, request):
        # authenticate user
        user, token = super().authenticate(request)

        # set user.ldap_user
        _LDAPUser(LDAPBackend(), user=user)

        # or:
        # user = LDAPBackend().get_user(user.id)

       return user, token

Useful to use caching along with it: AUTH_LDAP_CACHE_TIMEOUT

krnnrt
  • 33
  • 4