I'm using the ldap3
module and trying to create a simple Web App to search for users in an ldap server. The user (help desk people normally, doing the searching) must log in and input the user searched for.
The code so far creates/binds to the ldap server, and upon finding the searched user, a different page is displayed showing the user's credentials. So far so good.
On the page displaying the credentials, there is a search box, so that that the user can search again for another user. The problem I have now is how to remain logged in via ldap, so that the user only needs to input the searched user (and not again his username and password). I'm thinking I need to parse the conn
object in my returns, but somehow this seems a little clumsy. Here is my code:
views.py
def ldap_authentication(request):
if request.POST:
username = request.POST['username']
LDAP_MODIFY_PASS = request.POST['password']
searchFilter = request.POST['searchUser']
LDAP_AUTH_SEARCH_DN = '{}\\{}'.format(settings.DOMAIN_NAME, username)
conn = ldap_connect(request, un=LDAP_AUTH_SEARCH_DN, pw=LDAP_MODIFY_PASS)
attributes_list_keys = ['mail', 'givenName', 'employeeID',
'department', 'telephoneNumber', 'st', 'cn', 'l', 'title']
conn.search(
search_base=settings.LDAP_AUTH_SEARCH_BASE,
search_filter= '(cn={})'.format(searchFilter),
search_scope=SUBTREE,
attributes = attributes_list_keys
)
entry = conn.entries[0]
attributes_split = list(entry._attributes.values())
attr_keys = []
attr_values = []
for i in attributes_split:
attr_keys.append(i.key)
attr_values.append(i.value)
attributes = zip(attr_keys, attr_values)
return render(request, 'search_page.html', {'attributes':attributes})
return render(request, 'login.html')
def ldap_connect(request, un=None, pw=None):
try:
# Define the server
server = Server(settings.LDAP_SERVER, get_info=ALL)
# Connection and Bind operation
conn = Connection(server, user=un, password=pw, \
auto_bind=True,check_names=True)
conn.start_tls() # Session now on a secure channel.
return conn
except LDAPBindError as e:
print ("LDAPBindError, credentials incorrect: {0}".format(e))
logger.debug("LDAPBindError, credentials incorrect: {0}".format(e))
sys.exit(1)
except LDAPSocketOpenError as e:
print ("LDAPSocketOpenError, LDAP Server connection error: {0}".format(e))
logger.debug("LDAPSocketOpenError, LDAP Server connection error: {0}".format(e))
sys.exit(1)
def search_ldap_user(request):
if request.POST:
searchFilter = request.POST['searchUser']
print ("searchFilter_POST: {0}".format(searchFilter))
return render(request, 'login.html')
search_page.html
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="page-header">
<h2>LDAP Search Page</h2>
</div>
<p><p>
<div class="tab-content">
<div class="tab-pane active" id="tab1">
<table class="table table-striped table-condensed"
id="orders_open">
<thead>
<tr>
<th>Attribute in LDAP</th>
<th>Value</th>
</tr>
</thead>
{% for item1, item2 in attributes %}
<tr>
<td>{{ item1 }}</td>
<td>{{ item2 }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
<br><br><br><br>
<form method='post' action="{% url 'searchAnother' %}" class="form-signin">
{% csrf_token %}
<input type="text" class="form-control" name="searchUser"
placeholder="Search User" required=""/>
<button type="submit">Search</button>
</form>
<form method='post' action="" class="form-signin">{% csrf_token %}
<br>
<a href="{% url 'logout' %}" class="btn btn-lg btn-danger btn-
block">LogOut</a>
</form>
{% endblock content %}
login.html
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="page-header">
<h2>Welcome to LDAP Search. Please enter your credentials.</h2>
</div>
<div class="wrapper">
<form method='post' action="" class="form-signin">{% csrf_token %}
<h3 class="form-signin-heading">Please login</h3>
<input type="text" class="form-control" name="username"
placeholder="Username" required="" autofocus=""/>
<br>
<input type="password" class="form-control" name="password"
placeholder="Password" required=""/>
<br>
<input type="text" class="form-control" name="searchUser"
placeholder="Search User" required=""/>
<br>
<button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>
</form>
</div>
{% endblock content %}
I think the issue is somehow linking the search_ldap_user
function in my views.py
, with the search_page.html
via a conn object but it seems unclear how I am to do this. Has anyone any similar experience with using ldap to authenticate?
UPDATE
So I switched to the django_python3_ldap
library, with the following:
settings.py
DOMAIN_NAME = 'OurDomain'
LDAP_AUTH_URL = 'ldap://10.254.9.31:389'
LDAP_AUTH_USE_TLS = False
LDAP_AUTH_SEARCH_BASE = 'ou=company-Konzern, dc=ourdomain, dc=de'
LDAP_AUTH_OBJECT_CLASS = 'inetOrgPerson'
LDAP_AUTH_USER_FIELDS = {
"username": "cn",
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
}
# More info: https://github.com/etianen/django-python3-ldap
#LDAP_AUTH_USER_LOOKUP_FIELDS = ("username",)
LDAP_AUTH_USER_LOOKUP_FIELDS = ("cn",)
LDAP_AUTH_CLEAN_USER_DATA = "django_python3_ldap.utils.clean_user_data"
LDAP_AUTH_SYNC_USER_RELATIONS = "django_python3_ldap.utils.sync_user_relations"
LDAP_AUTH_FORMAT_SEARCH_FILTERS = "django_python3_ldap.utils.format_search_filters"
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_openldap"
LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN = 'OurDomain'
#LDAP_AUTH_CONNECTION_USERNAME = 'e123456'
LDAP_AUTH_CONNECTION_USERNAME = 'OurDomain\e123456'
LDAP_AUTH_CONNECTION_PASSWORD = 'abcdefghi'
LDAP_AUTH_CONNECT_TIMEOUT = None
LDAP_AUTH_RECEIVE_TIMEOUT = None
AUTHENTICATION_BACKENDS = (
'django_python3_ldap.auth.LDAPBackend',
'django.contrib.auth.backends.ModelBackend',
)
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_python3_ldap'
]
views.py
def ldap_login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
print ("username: {0}".format(username))
print ("password: {0}".format(password))
ldap_auth_search_dn = '{}\\{}'.format(settings.DOMAIN_NAME, username)
print ("ldap_auth_search_dn: {0}".format(ldap_auth_search_dn))
user = authenticate(username=username, password=password)
#user = authenticate(username=ldap_auth_search_dn, password=password)
print ("user: {0}".format(user))
if user and user.is_active:
print ("user.is_active!!")
login(request, user, backend='django_python3_ldap.auth.LDAPBackend')
return render(request, 'login_ldap.html')
As you can see from commented out lines, I've tried various permutations of configurations in the settings.py and there are still some issues unclear to me:
- Is this obligatory in settings.py:
LDAP_AUTH_OBJECT_CLASS
? - Which is the correct
LDAP_AUTH_USER_LOOKUP_FIELDS
to use? - Same as 2, but for
LDAP_AUTH_CONNECTION_USERNAME
(is this supposed to include the Domain)?
The following error message is thrown when I use the command
python ./manage.py ldap_sync_users
Error:
CommandError: Could not connect to LDAP server
When I don't use the ldap_sync_users
commands and simply run the server, there appears to be no connection, since the user
in the print statement comes back as None
. I know with the credentials that a connection works, as my previous code using the ldap3
library works. The only thing I can think of is the LDAP_AUTH_SEARCH_BASE
is not the same between both libraries.