pythondjangoauthenticationldapdjango-auth-ldap

LDAP: 'Populating Django user' causes error 'INSUFFICIENT_ACCESS'


I am attempting to authenticate a Django application against an LDAP server and am receiving some strange behavior. Please keep in mind I don't know much about LDAP so if I misuse some LDAP terminology, excuse me. Also note that throughout this question my_domain is the domain name of my company and user_id is the uid of the authenticating user.

Here is the pertinent part of my settings.py configuration file:

AUTHENTICATION_BACKENDS = [
    'django_auth_ldap.backend.LDAPBackend'
]
AUTH_LDAP_SERVER_URI = 'ldaps://ipa.my_domain.com:636'

AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,cn=users,cn=accounts,dc=my_domain,dc=com"

AUTH_LDAP_USER_FLAGS_BY_GROUP = {
    "is_active": "cn=all,cn=groups,cn=accounts,dc=my_domain,dc=com",
    "is_staff": "cn=all,cn=groups,cn=accounts,dc=my_domain,dc=com",
    "is_superuser": "cn=all,cn=groups,cn=accounts,dc=my_domain,dc=com"
}

AUTH_LDAP_ALWAYS_UPDATE_USER = True
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType()

AUTH_LDAP_GROUP_SEARCH = LDAPSearch("cn=groups,cn=accounts,dc=my_domain,dc=com",
    ldap.SCOPE_SUBTREE, "(objectClass=member)"
)

AUTH_LDAP_GLOBAL_OPTIONS = {
    ldap.OPT_X_TLS_REQUIRE_CERT: False,
    ldap.OPT_REFERRALS: False,
}

AUTH_LDAP_USER_ATTR_MAP = {"first_name": "givenName", "last_name": "sn"}

When I attempt to log in to my application, I receive this error:

Populating Django user user_id search_s('uid=user_id,cn=users,cn=accounts,dc=my_domain,dc=com', 0, '(objectClass=*)') returned 1 objects: uid=user_id,cn=users,cn=accounts,dc=my_domain,dc=com Caught LDAPError while authenticating user_id: INSUFFICIENT_ACCESS({'desc': 'Insufficient access'},)

However, when I flip this flag from True to false:

AUTH_LDAP_ALWAYS_UPDATE_USER = False

Authentication succeeds. Now here is the strange part: even though authentication succeeds, my attributes are not mapped to my Django User object (the ones specified in AUTH_LDAP_USER_ATTR_MAP = {"first_name": "givenName", "last_name": "sn"}). When I manually inspect request.user.ldap_user.attrs all the attributes are there.

Now here comes the question, what exactly does 'Populating Django user' mean? What is causing an 'INSUFFICIENT_ACCESS' error and why does flipping that one flag fix (hide?) the problem?

Thanks.


Solution

  • I was able to resolve all the issues by adding this line to my settings.py file:

    AUTH_LDAP_BIND_AS_AUTHENTICATING_USER = True
    

    It was my intention the entire time to bind as the authenticating user. I did not have a service account to bind on and certain information is unavailable if binding as an anonymous user with my company's LDAP setup. Apparently what was happening was after an authentication, a re-bind was occurring to AUTH_LDAP_BIND_DN which of course I had not specified, using an anonymous bind. That anonymous bind was not permitted to access group information and other details, causing the INSUFFICIENT_ACCESS error.

    From the documentation

    By default, all LDAP operations are performed with the AUTH_LDAP_BIND_DN and AUTH_LDAP_BIND_PASSWORD credentials, not with the user’s. Otherwise, the LDAP connection would be bound as the authenticating user during login requests and as the default credentials during other requests, so you might see inconsistent LDAP attributes depending on the nature of the Django view. If you’re willing to accept the inconsistency in order to retrieve attributes while bound as the authenticating user, see AUTH_LDAP_BIND_AS_AUTHENTICATING_USER.

    Addition details from the documentation about AUTH_LDAP_BIND_AS_AUTHENTICATING_USER:

    Default: False

    If True, authentication will leave the LDAP connection bound as the authenticating user, rather than forcing it to re-bind with the default credentials after authentication succeeds. This may be desirable if you do not have global credentials that are able to access the user’s attributes. django-auth-ldap never stores the user’s password, so this only applies to requests where the user is authenticated. Thus, the downside to this setting is that LDAP results may vary based on whether the user was authenticated earlier in the Django view, which could be surprising to code not directly concerned with authentication.