1

I setup an LDAP authentication to login to a Django admin site from an active directory(AD)

After logging in, the user are populated in the Users of Django admin site.

Is there a way to prevent that the users are populated in the Django admin site?

I though that AUTH_LDAP_USER_ATTR_MAP is what populates the user but I removed it and the users are still populated after logging in.

Here is the settings.py

import ldap
from django_auth_ldap.config import LDAPSearch, GroupOfNamesTypes, LDAPGroupQuery

AUTHENTICATION_BACKENDS = [
    'django_auth_ldap.backend.LDAPBackend',
    'django.contrib.auth.backends.ModelBackend'
]

AUTH_LDAP_SERVER_URI = "ldap://server.name"
AUTH_LDAP_BIND_AS_AUTHENTICATING_USER = True
AUTH_LDAP_BIND_DN = "cn=user,ou=group,dc=example,dc=example"
AUTH_LDAP_BIND_PASSWORD = "password"

AUTH_LDAP_GLOBAL_OPTIONS = {
    ldap.OPT_REFERRALS : False
}

AUTH_LDAP_USER_SEARCH = LDAPSearch(
    "dc=example,dc=com",
    ldap.SCOPE_SUBTREE,
    "(sAMAccountName=%(user)s)"
)

AUTH_LDAP_ALWAYS_UPDATE_USER = True

AUTH_LDAP_GROUP_TYPE = GroupOfNamesType(name_attr="cn")
AUTH_LDAP_MIRROR_GROUPS = True
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
    "ou=group,dc=example,dc=com",
    ldap.SCOPE_SUBTREE,
    "(objectClass=group)"

IS_STAFF_FLAG = (
    LDAPGroupQuery("cn=group,ou=group,dc=example,dc=com") |
    LDAPGroupQuery("cn=group,ou=group,dc=example,dc=com")
)

AUTH_LDAP_USER_FLAGS_BY_GROUP = {
    'is_staff': IS_STAFF_FLAG,
    'is_superuser': "cn=group,ou=group,dc=example,dc=example"
}

AUTH_LDAP_FIND_GROUP_PERMS = True
AUTH_LDAP_CACHE_TIMEOUT = 3600

Ricky Aguilar
  • 329
  • 1
  • 8
  • 19

1 Answers1

0

Since the goal is to "prevent that the users are populated in the Django admin site", setting AUTH_LDAP_ALWAYS_UPDATE_USER to False would not b what you want: that would stop LDAP authentication altogether for new users.

If your goal is to allow LDAP users to authenticate but not to automatically create a corresponding Django User model instance, you will need to customize the authentication backend to achieve this behavior.

For instance, custom subclass of LDAPBackend:

from django_auth_ldap.backend import LDAPBackend
from django.contrib.auth import get_user_model

class CustomLDAPBackend(LDAPBackend):
    
    def get_or_create_user(self, username, ldap_user):
        """
        That must return a (User, created) 2-tuple for the given
        LDAP user. You may use Django's built-in get_user_model()
        if you wish.
        """
        User = get_user_model()
        user = None
        created = False

        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            pass  # User will stay None

        return (user, created)

In this customized get_or_create_user method, it will only return a user object if it already exists in the Django database. If the user does not exist, it will return None, effectively avoiding the automatic creation of a new user.

To apply this custom backend, you need to update AUTHENTICATION_BACKENDS in your settings.py, as illustrated in the installation page:

AUTHENTICATION_BACKENDS = [
    'path.to.CustomLDAPBackend',  # Replace with the actual Python path to your subclass
    'django.contrib.auth.backends.ModelBackend',
]

With this setup, users who authenticate via LDAP will only be able to log in if they already have a corresponding user record in the Django database. The system will not automatically create a new Django user upon successful LDAP authentication.


I tried this, but after the LDAP user logged in, I checked in an admin user that the users are still populated in the Django admin site.

I created a backends.py file inside an app called authentication.

AUTHENTICATION_BACKENDS = [ >'authentication.backends.CustomLDAPBackend', >'django.contrib.auth.backends.ModelBackend', ]

Your AUTHENTICATION_BACKENDS setting appears to be correct, assuming that your CustomLDAPBackend class is defined in a file named backends.py within an app named authentication. If the users are still being populated, one possibility is that the CustomLDAPBackend is not being properly used. You can verify that it is being used by placing a debug log or a breakpoint in the get_or_create_user method to see if it is being executed during the login process.

Add logging imports to your backends.py:

import logging

logger = logging.getLogger(__name__)

Update the get_or_create_user method to include debug logs:

def get_or_create_user(self, username, ldap_user):
    User = get_user_model()
    user = None
    created = False

    try:
        user = User.objects.get(username=username)
        logger.debug(f"User {username} exists in Django, proceed to login.")
    except User.DoesNotExist:
        logger.debug(f"User {username} does not exist in Django, will not be created.")
        pass  # User will stay None

    return (user, created)

And make sure your Django logging settings can capture debug logs. Update your settings.py if necessary:

LOGGING = {
    # (your existing settings),
    'loggers': {
        # (your existing loggers),
        'authentication.backends': {
            'handlers': ['console'],
            'level': 'DEBUG',
        },
    },
}

Run your project and try to log in using an LDAP account. Then, check your logs to make sure that your get_or_create_user method is being called and see what it returns.

If after these steps you find that users are still being automatically created, then the issue might be elsewhere, possibly even outside the CustomLDAPBackend.


For the LOGGING in the settings.py, do I enclose all my settings inside? I tried putting it below the LDAP configurations, but it returns ValueError: dictionary doesn't specify a version.
The logger in the get_or_create_user does not return anything in the console after a user has logged in.

The LOGGING configuration in settings.py is usually a separate dictionary that defines how logging should be handled across the application. You do not have to enclose your other settings within it.

A basic example to include in your settings.py (that should work without conflicting with your LDAP settings) would be:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'root': {
        'handlers': ['console'],
        'level': 'WARNING',
    },
    'loggers': {
        'django': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': True,
        },
        'authentication.backends': {
            'handlers': ['console'],
            'level': 'DEBUG',
        },
    },
}

Make sure to include this as its own top-level key-value pair in your settings.py, not nested inside any other setting. That example sets up a basic logging configuration that outputs log messages to the console.

Also, the ValueError: dictionary does not specify a version error occurs when the LOGGING dictionary does not contain a 'version' key. That is required as per the configuration schema.

If the logger in the get_or_create_user method does not output anything, there are a few possibilities:

  • The method is not being called, which could mean that your custom backend is not being used.
  • There is an issue with your logging configuration.

After you have added the correct LOGGING settings, try to log in again and see if the debug messages are printed to the console. That will help verify whether your custom backend is actually being used.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • I tried this but after the LDAP user logged in, I checked in an admin user that the users are still populated in the Django admin site. I created a ```backends.py``` file inside an app called ```authentication```. So the ```AUTHENTICATION_BACKENDS = [ 'authentication.backends.CustomLDAPBackend', 'django.contrib.auth.backends.ModelBackend', ]``` right? – Ricky Aguilar Aug 28 '23 at 14:30
  • @RickyAguilar I have edited the answer to address your comment. – VonC Aug 28 '23 at 17:06
  • For the ```LOGGING``` in the ```settings.py```, do I enclose all my settings inside? I tried putting it below the LDAP configurations but it return ```ValueError: dictionary doesn't specify a version```. The logger in the ```get_or_create_user``` does not return anything in the console after a user has logged in. – Ricky Aguilar Aug 31 '23 at 16:29
  • @RickyAguilar I have edited the answer to address your comment. – VonC Aug 31 '23 at 17:29