add feature for deleting users and changing passwords with admin pass, many bug fixes, now it is fully tested

This commit is contained in:
2025-09-28 01:44:00 +02:00
parent 6bc05bcca3
commit f2cd767c5c
4 changed files with 58 additions and 23 deletions

View File

@@ -8,4 +8,4 @@ Depends: python3-flask, python3-ldap3, gunicorn, imagemagick, python3-passlib
Homepage: https://gitea.dmz.rs/fram3d/luser
Maintainer: fram3d <fram3d@dmz.rs>
Description: Web app that allows users to add,remove and change passwords in LDAP system
Version: 1.2.0
Version: 1.3.0

View File

@@ -4,5 +4,3 @@ LDAPADMINNAME = cn=admin,dc=example,dc=org
LDAPPASS = verysecr3t
USERBASE = ou=Users,dc=example,dc=org
CAPTCHA_PATH = /var/luser/luser/static/register/captcha_img/
ALTUSERBASE =
# ALTUSERBASE = ou=UsersAlt,dc=example,dc=org

View File

@@ -15,10 +15,39 @@ class LUSER():
base := string base in LDAP system where users are made
'''
def findlastlog(self):
'''
Return the largest uidNumber attribute of all users in base
'''
self.ldapconnection.search(search_base=self.logbase,search_filter=f'(objectClass=inetOrgPerson)', attributes=['uid'])
alllogs = self.ldapconnection.response
max = 0
for i in alllogs:
i_log = i['attributes']['uid']
if type(i_log) is list:
i_log = i_log[0]
if str(i_log) == 'total':
continue
if type(i_log) is str or type(i_log) is int:
i_log = int(i_log)
if i_log > max:
max = i_log
return max
def getlastlog(self)->int:
self.ldapconnection.search(search_base=f'uid=total,{self.logbase}',search_filter='(objectClass=person)', attributes=['uidNumber'])
response = self.ldapconnection.response
try:
self.ldapconnection.search(search_base=f'uid=total,{self.logbase}',search_filter='(objectClass=person)', attributes=['uidNumber'])
response = self.ldapconnection.response
except:
self.setlastlog(0)
if response == []:
self.setlastlog(0)
response = 0
else:
response = int(response[0]['attributes']['uidNumber'])
@@ -110,6 +139,7 @@ class LUSER():
self.ldapconnection = Connection(ldapserver, admin_user, admin_pass, auto_bind=True)
# uid and gid of most recently registered users
lastuidfound = self.findlastuid()
lastlogfound = self.findlastlog()
else:
self.ldapconnection = Connection(ldapserver, admin_user, admin_pass, auto_bind=False)
@@ -127,21 +157,24 @@ class LUSER():
self.lastgid = lastUID
else:
self.lastuid = lastuidfound
self.lastgid = lastuidfound
self.lastgid = lastuidfound
self.lastlog = lastlogfound
self.setlastlog(lastlogfound)
def prepare(self):
'''
Create base on LDAP host
'''
# Create dcObject on LDAP server and store boolean indicating it's success
# Create dcObject on LDAP server and store boolean indicating it's success
rcode1 = self.ldapconnection.add(self.dcfull, ['dcObject', 'organization'], {'o' : self.dc, 'dc' : self.dc})
# Create organizational units on LDAP server and store boolean indicating it's success
rcode2 = self.ldapconnection.add(self.base, ['top', 'organizationalUnit'], {'ou' : self.organization})
# Create organizational units for log on LDAP server and store boolean indicating it's success
rcode3 = self.ldapconnection.add(self.logbase, ['top', 'organizationalUnit'], {'ou' : self.organization})
rcode3 = self.ldapconnection.add(self.logbase, ['top', 'organizationalUnit'], {'ou' : 'log'})
return rcode1 and rcode2 and rcode3
@@ -159,14 +192,13 @@ class LUSER():
user := string containing username
password := string containing user password
'''
# Increase UID and GID counters
self.lastuid += 1
self.lastgid += 1
# Add user to base
id = f"uid={user}"
lastlog = self.getlastlog() + 1
lastlog = self.lastlog + 1
# Object classes of a user entry
objectClass = ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount', 'shadowAccount']
@@ -180,14 +212,16 @@ class LUSER():
# Add new user to log
attributes['description'] = 'ADD'
attributes['uid'] = str(lastlog)
if rcode1:
rcode2 = self.ldapconnection.add(f'uid={lastlog},{self.logbase}', objectClass, attributes)
rcode2 = self.ldapconnection.add(f'uid={str(lastlog)},{self.logbase}', objectClass, attributes)
else:
return False
if rcode2:
self.setlastlog(lastlog)
self.lastlog=lastlog
# Return True only if both entries was successful
return rcode1 and rcode2
@@ -214,7 +248,9 @@ class LUSER():
userdata = self.ldapconnection.response[0]
userdata['attributes']['description'] = 'CHANGEPASS'
lastlog = self.getlastlog() + 1
lastlog = self.lastlog + 1
userdata['attributes']['uid'] = str(lastlog)
chpassbool = self.ldapconnection.modify(f'uid={user},{self.base}', {'userPassword': (MODIFY_REPLACE,[newpass])})
chlastchangebool = self.ldapconnection.modify(f'uid={user},{self.base}', {'shadowLastChange' : (MODIFY_REPLACE,[self.lastpwchangenow()])})
@@ -224,6 +260,7 @@ class LUSER():
if rcode1:
self.setlastlog(lastlog)
self.lastlog = lastlog
return True
else:
return False
@@ -246,7 +283,9 @@ class LUSER():
userdata = self.ldapconnection.response[0]
userdata['attributes']['description'] = 'DELETE'
lastlog = self.getlastlog() + 1
lastlog = self.lastlog + 1
userdata['attributes']['uid'] = str(lastlog)
rcode1 = self.ldapconnection.delete(f'uid={user},{self.base}')
@@ -256,6 +295,7 @@ class LUSER():
if rcode2:
self.setlastlog(lastlog)
self.lastlog = lastlog
return True
else:
return False

View File

@@ -17,7 +17,6 @@ LDAPHOST = config.get('credentials', 'LDAPHOST')
LDAPADMINNAME = config.get('credentials', 'LDAPADMINNAME')
LDAPPASS = config.get('credentials', 'LDAPPASS')
USERBASE = config.get('credentials', 'USERBASE')
ALTUSERBASE = config.get('credentials', 'ALTUSERBASE')
CAPTCHA_PATH = config.get('credentials', 'CAPTCHA_PATH')
@app.route('/account/changepassword/', methods=['POST', 'GET'])
@@ -34,23 +33,22 @@ def changepassword():
return 'Error: password is too short'
# Create a LUSER connection
luser = LUSER(LDAPHOST,LDAPADMINNAME,LDAPPASS,USERBASE,ALTUSERBASE)
luser = LUSER(LDAPHOST,LDAPADMINNAME,LDAPPASS,USERBASE)
# Retrive current password
currentpassword = luser.getpassword(username)
if currentpassword == False:
return 'User doesn't exist'
return "User doesn't exist"
if ldap_salted_sha1.verify(oldpassword, currentpassword) == False and oldpassword != LDAPPASS:
return 'Wrong username/password combination'
ldaphash = ldap_salted_sha1.hash(newpassword)
althash = sha512_crypt.hash(newpassword)
# Try to change user password
try:
if luser.changepassword(username, ldaphash, althash) == True:
if luser.changepassword(username, ldaphash) == True:
return 'User password successfuly changed'
else:
return 'User password change failed'
@@ -68,9 +66,9 @@ def unregister():
password = request.form['password']
# Create a LUSER connection
luser = LUSER(LDAPHOST,LDAPADMINNAME,LDAPPASS,USERBASE,ALTUSERBASE)
luser = LUSER(LDAPHOST,LDAPADMINNAME,LDAPPASS,USERBASE)
if ldap_salted_sha1.verify(password, luser.getpassword(username)) == False:
if ldap_salted_sha1.verify(password, luser.getpassword(username)) == False and password != LDAPPASS:
return 'Wrong username/password combination'
# Try to delete user
@@ -141,14 +139,13 @@ def register():
return 'Error: username can only contain letters and numbers'
# Create a LUSER connection
luser = LUSER(LDAPHOST,LDAPADMINNAME,LDAPPASS,USERBASE,ALTUSERBASE)
luser = LUSER(LDAPHOST,LDAPADMINNAME,LDAPPASS,USERBASE)
# Try to add user
try:
ldaphash = ldap_salted_sha1.hash(password)
althash = sha512_crypt.hash(password)
#smtpctlout=subprocess.run(["smtpctl","encrypt", password],text=True,stdout=subprocess.PIPE)
#smtpdhash=smtpctlout.stdout[:-1]
if luser.add(username,ldaphash,althash):
if luser.add(username,ldaphash):
return 'User successfuly registered'
else:
return 'User registration failed, username probably taken'