From f2cd767c5c9794b305ad52ab96d38b493726db1b Mon Sep 17 00:00:00 2001 From: fram3d Date: Sun, 28 Sep 2025 01:44:00 +0200 Subject: [PATCH] add feature for deleting users and changing passwords with admin pass, many bug fixes, now it is fully tested --- build-deb/luser/DEBIAN/control | 2 +- luser/config.ini | 2 -- luser/models.py | 60 ++++++++++++++++++++++++++++------ luser/routes.py | 17 ++++------ 4 files changed, 58 insertions(+), 23 deletions(-) diff --git a/build-deb/luser/DEBIAN/control b/build-deb/luser/DEBIAN/control index fbf65ff..6ba376b 100644 --- a/build-deb/luser/DEBIAN/control +++ b/build-deb/luser/DEBIAN/control @@ -8,4 +8,4 @@ Depends: python3-flask, python3-ldap3, gunicorn, imagemagick, python3-passlib Homepage: https://gitea.dmz.rs/fram3d/luser Maintainer: fram3d Description: Web app that allows users to add,remove and change passwords in LDAP system -Version: 1.2.0 +Version: 1.3.0 diff --git a/luser/config.ini b/luser/config.ini index 0afa144..2abb824 100644 --- a/luser/config.ini +++ b/luser/config.ini @@ -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 diff --git a/luser/models.py b/luser/models.py index 01ea6f0..ef2034a 100644 --- a/luser/models.py +++ b/luser/models.py @@ -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 diff --git a/luser/routes.py b/luser/routes.py index 2d546e3..3e7fd0a 100644 --- a/luser/routes.py +++ b/luser/routes.py @@ -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'