How to troubleshoot a failing Django LDAP connection - django

I am using Django Rest with active directory authentication.
I have built a simple app with the following settings:
import ldap
from django_auth_ldap.config import LDAPSearch, GroupOfNamesType
# Baseline configuration.
AUTH_LDAP_SERVER_URI = 'ldap://ad_server.com'
AUTH_LDAP_BIND_DN = "CN=binduser,OU=Users,OU=ad_server,DC=ad_server,DC=com"
AUTH_LDAP_BIND_PASSWORD = "somepassword"
# Set up the basic group parameters.
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
"OU=Groups,OU=ad_server,DC=ad_server,DC=com",
ldap.SCOPE_SUBTREE,
"(objectClass=groupOfNames)",
)
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType(name_attr="cn")
# Simple group restrictions
AUTH_LDAP_REQUIRE_GROUP = "CN=prod,OU=Groups,OU=ad_server,DC=ad_server,DC=com"
AUTH_LDAP_DENY_GROUP = "cn=disabled,ou=django,ou=groups,dc=example,dc=com"
# Populate the Django user from the LDAP directory.
AUTH_LDAP_USER_ATTR_MAP = {
"first_name": "givenName",
"last_name": "sn",
'username': 'cn',
"email": "mail",
}
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
"is_active": "CN=prod,OU=Groups,OU=ad_server,DC=ad_server,DC=com",
"is_staff": "CN=prod,OU=Groups,OU=ad_server,DC=ad_server,DC=com",
"is_superuser": "CN=prod,OU=Groups,OU=ad_server,DC=ad_server,DC=com",
}
# This is the default, but I like to be explicit.
AUTH_LDAP_ALWAYS_UPDATE_USER = True
# Use LDAP group membership to calculate group permissions.
AUTH_LDAP_FIND_GROUP_PERMS = True
# Cache distinguished names and group memberships for an hour to minimize
# LDAP traffic.
AUTH_LDAP_CACHE_TIMEOUT = 3600
# Keep ModelBackend around for per-user permissions and maybe a local
# superuser.
AUTHENTICATION_BACKENDS = (
"django_auth_ldap.backend.LDAPBackend",
"django.contrib.auth.backends.ModelBackend",
)
Running an LDAP search from the cli works just fine
However when I try to authenticate with the http://localhost:8000/admin/ I get the following error.
The terminal on the other hand doesn't show anything:
There must be a better way to troubleshoot this, I have been at it all day and I can't figure it out.

add this in settings.py:
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {"console": {"class": "logging.StreamHandler"}},
"loggers": {"django_auth_ldap": {"level": "DEBUG", "handlers": ["console"]}},
}
logging

Related

Django saml2 login missing session variables

For my Django application, I am trying to enable SSO using Djangosaml2 and following are the versions I am using
djangosaml2==1.2.0
pysaml2==7.0.0
djangorestframework==3.12.2
Django==3.1.7
python==3.8
My saml2_settings is as follows
from os import path
import saml2
import saml2.saml
from app.local_settings import SERVER_URL
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'djangosaml2.backends.Saml2Backend',
)
SAML_SESSION_COOKIE_NAME = 'saml_session'
SAML_ATTRIBUTE_MAPPING = {
'username': ('username', ),
'email': ('email', ),
'first_name': ('first_name', ),
'last_name': ('last_name', ),
}
BASEDIR = path.dirname(path.abspath(__file__))
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
LOGIN_URL = '/saml2/login/'
LOGOUT_URL = '/saml2/logout/'
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
SAML_CREATE_UNKNOWN_USER = True
SAML_SERVER_URL = '10.23.1.114'
SAML_ENABLED = True
# MIDDLEWARE.append('djangosaml2.middleware.SamlSessionMiddleware')
SAML_CONFIG = {
# full path to the xmlsec1 binary programm
'xmlsec_binary': '/usr/bin/xmlsec1',
# your entity id, usually your subdomain plus the url to the metadata view
'entityid': path.join(SAML_SERVER_URL, 'saml2/metadata'),
# directory with attribute mapping
'attribute_map_dir': path.join(BASEDIR, 'attribute_maps'),
# this block states what services we provide
'service': {
# we are just a lonely SP
'sp' : {
'name': 'Dummy app',
'allow_unsolicited': True,
'authn_requests_signed': True,
'force_authn': True,
'want_response_signed': True,
'want_assertions_signed': True,
'logout_requests_signed': True,
'name_id_format_allow_create': False,
'endpoints': {
# url and binding to the assetion consumer service view
# do not change the binding or service name
'assertion_consumer_service': [
(path.join(SAML_SERVER_URL, 'saml2/acs/'),
saml2.BINDING_HTTP_POST),
],
# url and binding to the single logout service view
# do not change the binding or service name
'single_logout_service': [
(path.join(SAML_SERVER_URL, 'saml2/ls/'),
saml2.BINDING_HTTP_REDIRECT),
(path.join(SAML_SERVER_URL, 'saml2/ls/post/'),
saml2.BINDING_HTTP_POST),
],
},
},
},
# where the remote metadata is stored, local, remote or mdq server.
# One metadatastore or many ...
'metadata': {
'local': [path.join(BASEDIR, 'idp_metadata.xml')]
},
# Signing
'key_file': path.join(BASEDIR, 'samlkey.key'), # private part
'cert_file': path.join(BASEDIR, 'samlcert.pem'), # public part
# own metadata settings
'contact_person': [
{'given_name': '--',
'company': '--',
'email_address': '--',
'contact_type': '--'}
],
# you can set multilanguage information here
'organization': {
'name': [('--', 'en')],
'display_name': [('--', 'en')],
'url': [('--', 'en')],
},
"valid_for": 24
}
My middleware is as follows:
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.security.SecurityMiddleware',
'user_sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'app.middleware.APPMiddleware',
'djangosaml2.middleware.SamlSessionMiddleware'
]
With the above mentioned settings I am facing a couple of issues
After successfully authenticating from my SSO server by the time the request reaches my /login Url both the request.session and request.saml_session variables are getting reset and I am getting a complete new session ID. And also I am missing the saml_session attributes due to this issue. I did add a debug point in the djangosaml2 views and in there just before returning the response I could see the attributes present. But for some reason by the time the request reaches my app, its getting reset.
When I try to logout on saml2/logout, I am seeing the following error:
'NoneType' object has no attribute 'name_qualifier'
I cant seem to find what I am missing here. I have tried all I could think of but stuck. Any help is greatly appreciated. Thanks in advance.
I ended up doing the following two things, then it started working for me
Downgraded the djangosaml2 and pysaml version to 0.19.0 and 4.9.0 respectively.
For HTTPS connection, added SESSION_COOKIE_SECURE = True and for dev i.e. run server cases, SESSION_COOKIE_SECURE = False in your settings.py
I was migrating Django from 1.1 to 3.1. Your codebase actually fixed my issue. I added the SESSION_SERIALIZER in my saml/config.py:
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
And added SamlSessionMiddleware in settings.py:
djangosaml2.middleware.SamlSessionMiddleware
My issue was:
'WSGIRequest' object has no attribute 'saml session'

SAML2 with Django stuck on infinite loop

I am trying to use the djangosaml2 1.1.0 module in order to implement SSO login.
After the successful login, it gets stuck in an infinite loop trying to constantly login again.
I have tried to remove the #login_requried decorator, it works on then.
But, I need the #login_required decorator in order to prevent not logged in users to access specific views.
My believe is that django.contrib.auth.backends.ModelBackend is not properly configured with djangosaml2.backends.Saml2Backend
This is my code:
settings.py
SAML_CONFIG = {
# full path to the xmlsec1 binary programm
'xmlsec_binary': '/usr/bin/xmlsec1',
# your entity id, usually your subdomain plus the url to the metadata view
'entityid': 'http://localhost:8000/saml2/metadata/',
# directory with attribute mapping
'attribute_map_dir': path.join(BASEDIR, 'attribute-maps'),
# this block states what services we provide
'service': {
# we are just a lonely SP
'sp' : {
'name': 'Federated Django sample SP',
'name_id_format': saml2.saml.NAMEID_FORMAT_PERSISTENT,
# For Okta add signed logout requets. Enable this:
# "logout_requests_signed": True,
'endpoints': {
# url and binding to the assetion consumer service view
# do not change the binding or service name
'assertion_consumer_service': [
('http://localhost:8000/tacdb/items/',
saml2.BINDING_HTTP_POST),
],
# url and binding to the single logout service view
# do not change the binding or service name
'single_logout_service': [
# Disable next two lines for HTTP_REDIRECT for IDP's that only support HTTP_POST. Ex. Okta:
('http://localhost:8000/saml2/ls/',
saml2.BINDING_HTTP_REDIRECT),
('http://localhost:8000/saml2/ls/post',
saml2.BINDING_HTTP_POST),
],
},
# Mandates that the identity provider MUST authenticate the
# presenter directly rather than rely on a previous security context.
'force_authn': False,
# Enable AllowCreate in NameIDPolicy.
'name_id_format_allow_create': False,
# attributes that this project need to identify a user
'required_attributes': ['username'],
# attributes that may be useful to have but not required
'optional_attributes': ['eduPersonAffiliation'],
# in this section the list of IdPs we talk to are defined
# This is not mandatory! All the IdP available in the metadata will be considered.
'idp': {
# we do not need a WAYF service since there is
# only an IdP defined here. This IdP should be
# present in our metadata
# the keys of this dictionary are entity ids
'https://localhost/simplesaml/saml2/idp/metadata.php': {
'single_sign_on_service': {
saml2.BINDING_HTTP_REDIRECT: 'https://localhost/simplesaml/saml2/idp/SSOService.php',
},
'single_logout_service': {
saml2.BINDING_HTTP_REDIRECT: 'https://localhost/simplesaml/saml2/idp/SingleLogoutService.php',
},
},
},
},
},
LOGIN_URL = '/tacdb/saml2/login/'
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
MIDDLEWARE.append('djangosaml2.middleware.SamlSessionMiddleware')
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'djangosaml2.backends.Saml2Backend',
)
views.py
#csrf_exempt
#login_required
def item_list(request):
....
return render(
request, "items/item_list.html", {"filter": user_filter, "form": form}
)
urls.py
urlpatterns = [
url(
r"^tacdb/",
include(
[
path('', include('tacdashboard.urls')),
path('saml2/', include('djangosaml2.urls')),
]
)
)
]
The problem here was that I did not use the correct 'assertion_consumer_service':
'assertion_consumer_service': [
('http://localhost:8000/tacdb/saml2/acs',
saml2.BINDING_HTTP_POST),
],

Need help setting value for "is_staff" and "is_superuser" for Django LDAP authenticated users

I new to both Django and LDAP. I am creating a Django application with LDAP authentication as the login system. I am able to connect to the LDAP server and authenticate using a preexisting account from LDAP, which then populate my MySQL database auth_user table to have data row like this:
So my question is how can I set the value for "is_staff" and "is_superuser" to be 1?
-- LDAP setting inside settings.py --
AUTH_LDAP_SERVER_URI = "ldap://ldap.m*****.com:389"
AUTH_LDAP_BIND_DN = ""
AUTH_LDAP_BIND_PASSWORD = ""
AUTH_LDAP_USER_SEARCH = LDAPSearch("o=m*****net", ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
AUTH_LDAP_USER_ATTR_MAP = {
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
}
AUTHENTICATION_BACKENDS = (
'django_auth_ldap.backend.LDAPBackend',
'django.contrib.auth.backends.ModelBackend',
)
# logging
logger = logging.getLogger('django_auth_ldap')
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.DEBUG)
The setting AUTH_LDAP_USER_FLAGS_BY_GROUP can be used to match groups to the user flags
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
"is_active": "cn=active,ou=django,ou=groups,dc=example,dc=com",
"is_staff": "cn=staff,ou=django,ou=groups,dc=example,dc=com",
"is_superuser": "cn=superuser,ou=django,ou=groups,dc=example,dc=com",
}

django-auth-ldap failed authentication

I'm trying to use Django-Auth-Ldap in my project (Django 1.6, Python 2.7) but it is not working.
My active Directory shema is:
I've tested the connection on the cmd line by installing the ldap-utils package
sudo apt-get install ldap-utils
ldapsearch -H ldap://domain.com -D "ou=Resources,ou=Company, dc=domain,dc=com" -U "user_name" -w "user_password" -v -d 1
The connection test works fine.
I am using below code to test python-ldap connection from the shell:
import ldap
con = ldap.initialize('ldap://domain.com')
con.simple_bind_s('User_mail', 'User_password')
results = con.search_s('ou=Users,ou=Resources,ou=Company,dc=domain,dc=com', ldap.SCOPE_SUBTREE, "(cn=User_name)")
python-ldap connection works fine.
My problem is how to authenticate AD users from my django login interface?
settings.py:
import ldap
from django_auth_ldap.config import LDAPSearch
# The URL of the LDAP server.
AUTH_LDAP_SERVER_URI = "ldap://domain.com"
AUTH_LDAP_BIND_DN = "cn='User_name',ou=Resources,ou=Company,dc=domain,dc=com"
AUTH_LDAP_BIND_PASSWORD = "User_password"
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=Users,ou=Resources,ou=Company,dc=domain,dc=com",ldap.SCOPE_SUBTREE, "(cn=%(user)s)")
AUTH_LDAP_GLOBAL_OPTIONS = { ldap.OPT_REFERRALS : False }
AUTHENTICATION_BACKENDS = (
'django_auth_ldap.backend.LDAPBackend',
'django.contrib.auth.backends.ModelBackend',
)
views.py:
from django_auth_ldap.backend import LDAPBackend
auth = LDAPBackend()
user = auth.authenticate(username="User_name", password="User_password")
In the file django-ldap-debug.log I have this error:
Caught LDAPError while authenticating User_name: INVALID_CREDENTIALS({'info': '80090308: LdapErr: DSID-0C0903A9, comment: AcceptSecurityContext error, data 52e, v1db1', 'desc': 'Invalid credentials'},)
I found the answer.
I changed the AUTH_LDAP_BIND_DN by adding (OU=Users)
I must use samAccountName instead of CN in AUTH_LDAP_USER_SEARCH
My new settings.py :
import ldap, logging
from django_auth_ldap.config import LDAPSearch
logger = logging.getLogger('django_auth_ldap')
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.DEBUG)
AUTH_LDAP_SERVER_URI = "ldap://domain.com"
AUTH_LDAP_BIND_DN = "CN=User_name,OU=Users,OU=Resources,OU=Company,DC=domain,DC=com"
AUTH_LDAP_BIND_PASSWORD = "User_password"
AUTH_LDAP_USER_SEARCH = LDAPSearch("OU=Users,OU=Resources,OU=Company,DC=domain,DC=com",ldap.SCOPE_SUBTREE, "(samAccountName=%(user)s)")
AUTHENTICATION_BACKENDS = (
'django_auth_ldap.backend.LDAPBackend',
'django.contrib.auth.backends.ModelBackend',
)
My views.py
from django_auth_ldap.backend import LDAPBackend
def login(request):
if request.method == 'POST':
form = MyLoginForm(data=request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
auth = LDAPBackend()
user = auth.authenticate(username=username, password=password)
if user is not None:
....
else:
form = MyLoginForm()
....
Hope this help all :)
I had similar problem but I have solved it other way around. Under AUTH_LDAP_BIND_DN have just put the user and domain and password. After that this was working like charm...
While I was investigating my ldaps issues I found above solution somehow useful, maybe also someone will benefit with my solution
LDAP_IGNORE_CERT_ERRORS = True
AUTH_LDAP_START_TLS = False
AUTH_LDAP_SERVER_URI = "ldaps://domain.com:636"
AUTH_LDAP_BIND_DN = "serviceaccount#domain.com"
AUTH_LDAP_BIND_PASSWORD = "superPass"
AUTH_LDAP_USER_SEARCH = LDAPSearch(
"OU=Company,DC=domain,DC=com",ldap.SCOPE_SUBTREE,"(sAMAccountName=%(user)s)"
)
AUTH_LDAP_USER_ATTR_MAP = {
"first_name": "givenName",
"last_name": "sn",
"email": "mail"
}

how to use ldap for authentication for app in django?

I want to use LDAP authentication for my application. My application is taking the input from user and storing details such as firstname,lastname in database. When I write following code in my settings.py file but I didn't get any error for that and application is running normally. So how can I know that LDAP is using in app or need some modifications in app. Please help me. I used basic settings from Django documentation.
import ldap
from django_auth_ldap.config import LDAPSearch, GroupOfNamesType
# Baseline configuration.
AUTH_LDAP_SERVER_URI = "ldap://ldap.example.com"
AUTH_LDAP_BIND_DN = "cn=django-agent,dc=example,dc=com"
AUTH_LDAP_BIND_PASSWORD = "marksheet"
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=django,ou=groups,dc=example,dc=com",
ldap.SCOPE_SUBTREE, "(objectClass=groupOfNames)"
)
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType(name_attr="cn")
AUTH_LDAP_REQUIRE_GROUP = "cn=enabled,ou=django,ou=groups,dc=example,dc=com"
AUTH_LDAP_USER_ATTR_MAP = {
"first_name": "firstname",
"last_name": "lastname",
}
Thanks...
It seems you are trying to do this using the django-auth-ldap package. For this setup, your settings are missing the most important part, namely adding django_auth_ldap.backend.LDAPBackend to your AUTHENTICATION_BACKENDS.
Refer to the django-auth-ldap documentation for more detailed setup instructions.