|
|
|
@ -18,83 +18,75 @@ from datetime import datetime
|
|
|
|
|
from random import choice, randint |
|
|
|
|
import string |
|
|
|
|
|
|
|
|
|
from django.conf import settings |
|
|
|
|
from django_auth_ldap.backend import LDAPBackend |
|
|
|
|
from ldap3 import Server, ServerPool, Connection, ObjectDef, AttrDef, Reader, Writer |
|
|
|
|
|
|
|
|
|
import os |
|
|
|
|
|
|
|
|
|
# Use ldap, like django_auth_backend |
|
|
|
|
import ldap |
|
|
|
|
|
|
|
|
|
def check_user_exists(username): |
|
|
|
|
user = LDAPBackend().populate_user(username) |
|
|
|
|
from django.conf import settings |
|
|
|
|
|
|
|
|
|
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" |
|
|
|
|
|
|
|
|
|
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)) |
|
|
|
|
self.conn = ldap.initialize(self.uri) |
|
|
|
|
if settings.AUTH_LDAP_START_TLS: |
|
|
|
|
self.conn.start_tls_s() |
|
|
|
|
|
|
|
|
|
# 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) |
|
|
|
|
print("{} {} {}".format(self.uri, self.user, self.password)) |
|
|
|
|
self.conn.bind_s(self.user, self.password) |
|
|
|
|
|
|
|
|
|
# Filling in some of the data |
|
|
|
|
# required attributes are sn, cn, homeDirectory, uid (already handled by dn), uidNumber, gidNumber |
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
|
# 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 |
|
|
|
|
|
|
|
|
|
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']) |
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
# New uid is highest old uidnumber plus one |
|
|
|
|
newuid = (sorted(uidlist)[len(uidlist)-1] + 1) |
|
|
|
|
return newuid |
|
|
|
|
def create_user(self, user, password, firstname, lastname, email): |
|
|
|
|
dn = self.dn.format(user) |
|
|
|
|
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)) |
|
|
|
|
|
|
|
|
|
def get_new_uid_number(self): |
|
|
|
|
uidlist = [0] |
|
|
|
|
|
|
|
|
|
for result in self.conn.search_s(self.search_base, |
|
|
|
|
self.search_scope, |
|
|
|
|
self.search_filter): |
|
|
|
|
|
|
|
|
|
uidlist.append(int(result[1]['uidNumber'][0])) |
|
|
|
|
|
|
|
|
|
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' |
|
|
|
|