Try ldap auth using python-ldap
This commit is contained in:
parent
8cd904dcdc
commit
93677e6ad6
4 changed files with 69 additions and 78 deletions
|
@ -4,6 +4,7 @@ LDAPSERVER="ldap://ldap1.ungleich.ch ldap://ldap2.ungleich.ch"
|
||||||
LDAPSEARCHUSER="user here"
|
LDAPSEARCHUSER="user here"
|
||||||
LDAPSEARCHUSERPASSWORD="password here"
|
LDAPSEARCHUSERPASSWORD="password here"
|
||||||
|
|
||||||
|
|
||||||
# Space separated list of search bases for users
|
# Space separated list of search bases for users
|
||||||
LDAPSEARCH="ou=users,dc=ungleich,dc=ch ou=customers,dc=ungleich,dc=ch"
|
LDAPSEARCH="ou=users,dc=ungleich,dc=ch ou=customers,dc=ungleich,dc=ch"
|
||||||
LDAPCREATE="ou=customers,dc=ungleich,dc=ch"
|
LDAPCREATE="ou=customers,dc=ungleich,dc=ch"
|
||||||
|
|
|
@ -28,6 +28,8 @@ search_base = os.environ['LDAPSEARCH'].split()
|
||||||
search_base_ldap = [ LDAPSearch(x, ldap.SCOPE_SUBTREE, "(uid=%(user)s)") for x in search_base ]
|
search_base_ldap = [ LDAPSearch(x, ldap.SCOPE_SUBTREE, "(uid=%(user)s)") for x in search_base ]
|
||||||
AUTH_LDAP_USER_SEARCH = LDAPSearchUnion(*search_base_ldap)
|
AUTH_LDAP_USER_SEARCH = LDAPSearchUnion(*search_base_ldap)
|
||||||
|
|
||||||
|
if os.environ['LDAP_USE_TLS']:
|
||||||
|
AUTH_LDAP_START_TLS=True
|
||||||
|
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
|
138
dal/dal/views.py
138
dal/dal/views.py
|
@ -18,83 +18,75 @@ from datetime import datetime
|
||||||
from random import choice, randint
|
from random import choice, randint
|
||||||
import string
|
import string
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Use ldap, like django_auth_backend
|
||||||
|
import ldap
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django_auth_ldap.backend import LDAPBackend
|
|
||||||
from ldap3 import Server, ServerPool, Connection, ObjectDef, AttrDef, Reader, Writer
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def check_user_exists(username):
|
|
||||||
user = LDAPBackend().populate_user(username)
|
|
||||||
|
|
||||||
if not user == None:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class LDAP(object):
|
class LDAP(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.server = settings.AUTH_LDAP_SERVER_URI
|
self.uri = settings.AUTH_LDAP_SERVER_URI
|
||||||
self.user = settings.AUTH_LDAP_BIND_DN
|
self.user = settings.AUTH_LDAP_BIND_DN
|
||||||
self.password = settings.AUTH_LDAP_BIND_PASSWORD
|
self.password = settings.AUTH_LDAP_BIND_PASSWORD
|
||||||
|
|
||||||
# FIXME: take from settings
|
# FIXME: take from settings
|
||||||
self.search = os.environ['LDAPSEARCH']
|
self.search_base = os.environ['LDAPSEARCH']
|
||||||
|
self.search_scope = ldap.SCOPE_SUBTREE
|
||||||
|
self.search_filter = "objectClass=inetOrgPerson"
|
||||||
|
|
||||||
# FIXME: hard coded
|
# FIXME: hard coded
|
||||||
self.dn = "uid={{}},{}".format(os.environ['LDAPCREATE'])
|
self.dn = "uid={{}},{}".format(os.environ['LDAPCREATE'])
|
||||||
|
self.gid = "10004"
|
||||||
|
|
||||||
|
self.conn = ldap.initialize(self.uri)
|
||||||
|
if settings.AUTH_LDAP_START_TLS:
|
||||||
|
self.conn.start_tls_s()
|
||||||
|
|
||||||
|
print("{} {} {}".format(self.uri, self.user, self.password))
|
||||||
|
self.conn.bind_s(self.user, self.password)
|
||||||
|
|
||||||
|
|
||||||
|
def check_user_exists(self, username):
|
||||||
|
result = self.conn.search_s(self.search_base,
|
||||||
|
self.search_scope,
|
||||||
|
self.dn.format(username))
|
||||||
|
if not len(result) == 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def create_user(self, user, password, firstname, lastname, email):
|
def create_user(self, user, password, firstname, lastname, email):
|
||||||
conn = Connection(self.server,
|
|
||||||
self.user,
|
|
||||||
self.password)
|
|
||||||
|
|
||||||
if not conn.bind():
|
|
||||||
raise Exception("Could not connect to LDAPserver {}".format(self.server))
|
|
||||||
|
|
||||||
# set objectClasses for the new user
|
|
||||||
obj_new_user = ObjectDef(['inetOrgPerson', 'posixAccount', 'ldapPublicKey'], conn)
|
|
||||||
|
|
||||||
w = Writer(conn, obj_new_user)
|
|
||||||
dn = self.dn.format(user)
|
dn = self.dn.format(user)
|
||||||
w.new(dn)
|
modlist = {
|
||||||
|
"objectClass": ["inetOrgPerson", "posixAccount", "ldapPublickey"],
|
||||||
|
"uid": [user],
|
||||||
|
"sn": [lastname],
|
||||||
|
"givenName": [firstname],
|
||||||
|
"cn": ["{} {}".format(firstname, lastname)],
|
||||||
|
"displayName": ["{} {}".format(firstname, lastname)],
|
||||||
|
"uidNumber": ["{}".format(self.get_new_uid_number(conn))],
|
||||||
|
"gidNumber": [self.gid],
|
||||||
|
"loginShell": ["/bin/bash"],
|
||||||
|
"homeDirectory": ["/home/{}".format(user)],
|
||||||
|
"mail": email,
|
||||||
|
"userPassword": password
|
||||||
|
}
|
||||||
|
result = self.conn.add_s(dn, ldap.modlist.addModlist(modlist))
|
||||||
|
|
||||||
# Filling in some of the data
|
def get_new_uid_number(self):
|
||||||
# required attributes are sn, cn, homeDirectory, uid (already handled by dn), uidNumber, gidNumber
|
uidlist = [0]
|
||||||
|
|
||||||
w[0].givenName = firstname
|
for result in self.conn.search_s(self.search_base,
|
||||||
w[0].sn = lastname
|
self.search_scope,
|
||||||
w[0].cn = firstname + " " + lastname
|
self.search_filter):
|
||||||
w[0].mail = email
|
|
||||||
w[0].userPassword = password
|
|
||||||
w[0].homeDirectory = "/home/{}".format(user)
|
|
||||||
|
|
||||||
# Set uidNumber as last used uidNumber+1
|
uidlist.append(int(result[1]['uidNumber'][0]))
|
||||||
w[0].uidNumber = self.get_new_uid_number(conn)
|
|
||||||
# gidNumber for users created by userservice, nice and clear
|
|
||||||
w[0].gidNumber = 10004
|
|
||||||
|
|
||||||
if not w.commit():
|
return sorted(uidlist)[-1] + 1
|
||||||
conn.unbind()
|
|
||||||
raise Exception("Could not write new user {} to LDAP DB, commit error".format(user))
|
|
||||||
|
|
||||||
conn.unbind()
|
|
||||||
|
|
||||||
# Function to get the next uid number. Not elegant, but LAM does it too and didn't really find anything
|
|
||||||
# nicer. The sorted() seems to be quite efficient, so it shouldn't take too long even on larger arrays
|
|
||||||
def get_new_uid_number(self, conn):
|
|
||||||
newuid = 0
|
|
||||||
uidlist = []
|
|
||||||
|
|
||||||
for search in self.search.split():
|
|
||||||
conn.search(search, '(&(objectClass=posixAccount)(uidNumber=*))', attributes = [ 'uidNumber' ])
|
|
||||||
for c in conn.response:
|
|
||||||
uidlist.append(c['attributes']['uidNumber'])
|
|
||||||
|
|
||||||
# New uid is highest old uidnumber plus one
|
|
||||||
newuid = (sorted(uidlist)[len(uidlist)-1] + 1)
|
|
||||||
return newuid
|
|
||||||
|
|
||||||
class Index(View):
|
class Index(View):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
@ -118,36 +110,40 @@ class Register(View):
|
||||||
|
|
||||||
# Someone filled out the register page, do some basic checks and throw it at nameko
|
# Someone filled out the register page, do some basic checks and throw it at nameko
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
# message for the error template
|
l = LDAP()
|
||||||
|
|
||||||
service = 'register an user'
|
service = 'register an user'
|
||||||
# urlname for 'go back' on the errorpage
|
|
||||||
urlname = 'register'
|
urlname = 'register'
|
||||||
username = request.POST.get('username')
|
username = request.POST.get('username')
|
||||||
|
|
||||||
if username == "" or not username:
|
if username == "" or not username:
|
||||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Please supply a username.' } )
|
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Please supply a username.' } )
|
||||||
|
|
||||||
if check_user_exists(username):
|
|
||||||
|
if l.check_user_exists(username):
|
||||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'User already exists.' } )
|
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'User already exists.' } )
|
||||||
|
|
||||||
password1 = request.POST.get('password1')
|
password1 = request.POST.get('password1')
|
||||||
password2 = request.POST.get('password2')
|
password2 = request.POST.get('password2')
|
||||||
|
|
||||||
# check if the supplied passwords match
|
|
||||||
if password1 != password2:
|
if password1 != password2:
|
||||||
return render(request, 'error.html', { 'urlname': urlname,
|
return render(request, 'error.html', { 'urlname': urlname,
|
||||||
'service': service,
|
'service': service,
|
||||||
'error': 'Your passwords did not match. Please supply the same password twice.' } )
|
'error': "Passwords don't match." } )
|
||||||
|
|
||||||
email = request.POST.get('email')
|
email = request.POST.get('email')
|
||||||
# Is the emailaddress valid?
|
|
||||||
try:
|
try:
|
||||||
validate_email(email)
|
validate_email(email)
|
||||||
except ValidationError:
|
except ValidationError:
|
||||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'The supplied email address is invalid.' } )
|
return render(request, 'error.html', { 'urlname': urlname,
|
||||||
|
'service': service,
|
||||||
|
'error': 'The supplied email address is invalid.' } )
|
||||||
|
|
||||||
firstname = request.POST.get('firstname')
|
firstname = request.POST.get('firstname')
|
||||||
lastname = request.POST.get('lastname')
|
lastname = request.POST.get('lastname')
|
||||||
if firstname == "" or not firstname or lastname == "" or not lastname:
|
if not firstname or not lastname:
|
||||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Please enter your firstname and lastname.' } )
|
return render(request, 'error.html', { 'urlname': urlname,
|
||||||
|
'service': service,
|
||||||
|
'error': 'Please enter your firstname and lastname.' } )
|
||||||
|
|
||||||
# so nothing strange happens if there are escapable chars
|
# so nothing strange happens if there are escapable chars
|
||||||
pwd = r'%s' % password1
|
pwd = r'%s' % password1
|
||||||
|
@ -163,7 +159,6 @@ class Register(View):
|
||||||
|
|
||||||
return render(request, 'usercreated.html', { 'user': username } )
|
return render(request, 'usercreated.html', { 'user': username } )
|
||||||
|
|
||||||
|
|
||||||
class ChangeData(View):
|
class ChangeData(View):
|
||||||
# provide the form for the change request
|
# provide the form for the change request
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
@ -220,17 +215,10 @@ class ChangeData(View):
|
||||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': result } )
|
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': result } )
|
||||||
|
|
||||||
|
|
||||||
# Resets the password for a user
|
|
||||||
# Sends email to the user with a link to reset the password
|
|
||||||
|
|
||||||
class ResetPassword(View):
|
class ResetPassword(View):
|
||||||
|
|
||||||
# Presents the form with some information
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
return render(request, 'resetpassword.html')
|
return render(request, 'resetpassword.html')
|
||||||
|
|
||||||
# gets the data from confirming the reset request and checks if it was not a misclick
|
|
||||||
# (by having the user type in his username)
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
urlname = 'reset_password'
|
urlname = 'reset_password'
|
||||||
service = 'send a password reset request'
|
service = 'send a password reset request'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
django>=2.1.2
|
django
|
||||||
django-auth-ldap>=1.7.0
|
django-auth-ldap
|
||||||
ldap3>=2.5.1
|
python-ldap
|
||||||
django-dotenv
|
django-dotenv
|
||||||
|
|
Loading…
Reference in a new issue