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"
|
||||
LDAPSEARCHUSERPASSWORD="password here"
|
||||
|
||||
|
||||
# Space separated list of search bases for users
|
||||
LDAPSEARCH="ou=users,dc=ungleich,dc=ch 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 ]
|
||||
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__)))
|
||||
|
||||
|
|
138
dal/dal/views.py
138
dal/dal/views.py
|
@ -18,83 +18,75 @@ from datetime import datetime
|
|||
from random import choice, randint
|
||||
import string
|
||||
|
||||
import os
|
||||
|
||||
# Use ldap, like django_auth_backend
|
||||
import ldap
|
||||
|
||||
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):
|
||||
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.password = settings.AUTH_LDAP_BIND_PASSWORD
|
||||
|
||||
# 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
|
||||
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):
|
||||
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)
|
||||
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
|
||||
# required attributes are sn, cn, homeDirectory, uid (already handled by dn), uidNumber, gidNumber
|
||||
def get_new_uid_number(self):
|
||||
uidlist = [0]
|
||||
|
||||
w[0].givenName = firstname
|
||||
w[0].sn = lastname
|
||||
w[0].cn = firstname + " " + lastname
|
||||
w[0].mail = email
|
||||
w[0].userPassword = password
|
||||
w[0].homeDirectory = "/home/{}".format(user)
|
||||
for result in self.conn.search_s(self.search_base,
|
||||
self.search_scope,
|
||||
self.search_filter):
|
||||
|
||||
# Set uidNumber as last used uidNumber+1
|
||||
w[0].uidNumber = self.get_new_uid_number(conn)
|
||||
# gidNumber for users created by userservice, nice and clear
|
||||
w[0].gidNumber = 10004
|
||||
uidlist.append(int(result[1]['uidNumber'][0]))
|
||||
|
||||
if not w.commit():
|
||||
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
|
||||
return sorted(uidlist)[-1] + 1
|
||||
|
||||
class Index(View):
|
||||
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
|
||||
def post(self, request):
|
||||
# message for the error template
|
||||
l = LDAP()
|
||||
|
||||
service = 'register an user'
|
||||
# urlname for 'go back' on the errorpage
|
||||
urlname = 'register'
|
||||
username = request.POST.get('username')
|
||||
|
||||
if username == "" or not 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.' } )
|
||||
|
||||
password1 = request.POST.get('password1')
|
||||
password2 = request.POST.get('password2')
|
||||
|
||||
# check if the supplied passwords match
|
||||
if password1 != password2:
|
||||
return render(request, 'error.html', { 'urlname': urlname,
|
||||
'service': service,
|
||||
'error': 'Your passwords did not match. Please supply the same password twice.' } )
|
||||
'error': "Passwords don't match." } )
|
||||
|
||||
email = request.POST.get('email')
|
||||
# Is the emailaddress valid?
|
||||
try:
|
||||
validate_email(email)
|
||||
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')
|
||||
lastname = request.POST.get('lastname')
|
||||
if firstname == "" or not firstname or lastname == "" or not lastname:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Please enter your firstname and lastname.' } )
|
||||
if not firstname or not 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
|
||||
pwd = r'%s' % password1
|
||||
|
@ -163,7 +159,6 @@ class Register(View):
|
|||
|
||||
return render(request, 'usercreated.html', { 'user': username } )
|
||||
|
||||
|
||||
class ChangeData(View):
|
||||
# provide the form for the change request
|
||||
def get(self, request):
|
||||
|
@ -220,17 +215,10 @@ class ChangeData(View):
|
|||
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):
|
||||
|
||||
# Presents the form with some information
|
||||
def get(self, request):
|
||||
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):
|
||||
urlname = 'reset_password'
|
||||
service = 'send a password reset request'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
django>=2.1.2
|
||||
django-auth-ldap>=1.7.0
|
||||
ldap3>=2.5.1
|
||||
django
|
||||
django-auth-ldap
|
||||
python-ldap
|
||||
django-dotenv
|
||||
|
|
Loading…
Reference in a new issue