add feature for deleting users and changing passwords with admin pass, many bug fixes, now it is fully tested
This commit is contained in:
@@ -8,4 +8,4 @@ Depends: python3-flask, python3-ldap3, gunicorn, imagemagick, python3-passlib
|
|||||||
Homepage: https://gitea.dmz.rs/fram3d/luser
|
Homepage: https://gitea.dmz.rs/fram3d/luser
|
||||||
Maintainer: fram3d <fram3d@dmz.rs>
|
Maintainer: fram3d <fram3d@dmz.rs>
|
||||||
Description: Web app that allows users to add,remove and change passwords in LDAP system
|
Description: Web app that allows users to add,remove and change passwords in LDAP system
|
||||||
Version: 1.2.0
|
Version: 1.3.0
|
||||||
|
@@ -4,5 +4,3 @@ LDAPADMINNAME = cn=admin,dc=example,dc=org
|
|||||||
LDAPPASS = verysecr3t
|
LDAPPASS = verysecr3t
|
||||||
USERBASE = ou=Users,dc=example,dc=org
|
USERBASE = ou=Users,dc=example,dc=org
|
||||||
CAPTCHA_PATH = /var/luser/luser/static/register/captcha_img/
|
CAPTCHA_PATH = /var/luser/luser/static/register/captcha_img/
|
||||||
ALTUSERBASE =
|
|
||||||
# ALTUSERBASE = ou=UsersAlt,dc=example,dc=org
|
|
||||||
|
@@ -15,10 +15,39 @@ class LUSER():
|
|||||||
base := string base in LDAP system where users are made
|
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:
|
def getlastlog(self)->int:
|
||||||
self.ldapconnection.search(search_base=f'uid=total,{self.logbase}',search_filter='(objectClass=person)', attributes=['uidNumber'])
|
try:
|
||||||
response = self.ldapconnection.response
|
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 == []:
|
if response == []:
|
||||||
|
self.setlastlog(0)
|
||||||
response = 0
|
response = 0
|
||||||
else:
|
else:
|
||||||
response = int(response[0]['attributes']['uidNumber'])
|
response = int(response[0]['attributes']['uidNumber'])
|
||||||
@@ -110,6 +139,7 @@ class LUSER():
|
|||||||
self.ldapconnection = Connection(ldapserver, admin_user, admin_pass, auto_bind=True)
|
self.ldapconnection = Connection(ldapserver, admin_user, admin_pass, auto_bind=True)
|
||||||
# uid and gid of most recently registered users
|
# uid and gid of most recently registered users
|
||||||
lastuidfound = self.findlastuid()
|
lastuidfound = self.findlastuid()
|
||||||
|
lastlogfound = self.findlastlog()
|
||||||
else:
|
else:
|
||||||
self.ldapconnection = Connection(ldapserver, admin_user, admin_pass, auto_bind=False)
|
self.ldapconnection = Connection(ldapserver, admin_user, admin_pass, auto_bind=False)
|
||||||
|
|
||||||
@@ -127,21 +157,24 @@ class LUSER():
|
|||||||
self.lastgid = lastUID
|
self.lastgid = lastUID
|
||||||
else:
|
else:
|
||||||
self.lastuid = lastuidfound
|
self.lastuid = lastuidfound
|
||||||
self.lastgid = lastuidfound
|
self.lastgid = lastuidfound
|
||||||
|
|
||||||
|
self.lastlog = lastlogfound
|
||||||
|
self.setlastlog(lastlogfound)
|
||||||
|
|
||||||
def prepare(self):
|
def prepare(self):
|
||||||
'''
|
'''
|
||||||
Create base on LDAP host
|
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})
|
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
|
# Create organizational units on LDAP server and store boolean indicating it's success
|
||||||
rcode2 = self.ldapconnection.add(self.base, ['top', 'organizationalUnit'], {'ou' : self.organization})
|
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
|
# 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
|
return rcode1 and rcode2 and rcode3
|
||||||
|
|
||||||
@@ -159,14 +192,13 @@ class LUSER():
|
|||||||
user := string containing username
|
user := string containing username
|
||||||
password := string containing user password
|
password := string containing user password
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Increase UID and GID counters
|
# Increase UID and GID counters
|
||||||
self.lastuid += 1
|
self.lastuid += 1
|
||||||
self.lastgid += 1
|
self.lastgid += 1
|
||||||
|
|
||||||
# Add user to base
|
# Add user to base
|
||||||
id = f"uid={user}"
|
id = f"uid={user}"
|
||||||
lastlog = self.getlastlog() + 1
|
lastlog = self.lastlog + 1
|
||||||
|
|
||||||
# Object classes of a user entry
|
# Object classes of a user entry
|
||||||
objectClass = ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount', 'shadowAccount']
|
objectClass = ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount', 'shadowAccount']
|
||||||
@@ -180,14 +212,16 @@ class LUSER():
|
|||||||
|
|
||||||
# Add new user to log
|
# Add new user to log
|
||||||
attributes['description'] = 'ADD'
|
attributes['description'] = 'ADD'
|
||||||
|
attributes['uid'] = str(lastlog)
|
||||||
|
|
||||||
if rcode1:
|
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:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if rcode2:
|
if rcode2:
|
||||||
self.setlastlog(lastlog)
|
self.setlastlog(lastlog)
|
||||||
|
self.lastlog=lastlog
|
||||||
|
|
||||||
# Return True only if both entries was successful
|
# Return True only if both entries was successful
|
||||||
return rcode1 and rcode2
|
return rcode1 and rcode2
|
||||||
@@ -214,7 +248,9 @@ class LUSER():
|
|||||||
userdata = self.ldapconnection.response[0]
|
userdata = self.ldapconnection.response[0]
|
||||||
userdata['attributes']['description'] = 'CHANGEPASS'
|
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])})
|
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()])})
|
chlastchangebool = self.ldapconnection.modify(f'uid={user},{self.base}', {'shadowLastChange' : (MODIFY_REPLACE,[self.lastpwchangenow()])})
|
||||||
@@ -224,6 +260,7 @@ class LUSER():
|
|||||||
|
|
||||||
if rcode1:
|
if rcode1:
|
||||||
self.setlastlog(lastlog)
|
self.setlastlog(lastlog)
|
||||||
|
self.lastlog = lastlog
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
@@ -246,7 +283,9 @@ class LUSER():
|
|||||||
userdata = self.ldapconnection.response[0]
|
userdata = self.ldapconnection.response[0]
|
||||||
userdata['attributes']['description'] = 'DELETE'
|
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}')
|
rcode1 = self.ldapconnection.delete(f'uid={user},{self.base}')
|
||||||
@@ -256,6 +295,7 @@ class LUSER():
|
|||||||
|
|
||||||
if rcode2:
|
if rcode2:
|
||||||
self.setlastlog(lastlog)
|
self.setlastlog(lastlog)
|
||||||
|
self.lastlog = lastlog
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
@@ -17,7 +17,6 @@ LDAPHOST = config.get('credentials', 'LDAPHOST')
|
|||||||
LDAPADMINNAME = config.get('credentials', 'LDAPADMINNAME')
|
LDAPADMINNAME = config.get('credentials', 'LDAPADMINNAME')
|
||||||
LDAPPASS = config.get('credentials', 'LDAPPASS')
|
LDAPPASS = config.get('credentials', 'LDAPPASS')
|
||||||
USERBASE = config.get('credentials', 'USERBASE')
|
USERBASE = config.get('credentials', 'USERBASE')
|
||||||
ALTUSERBASE = config.get('credentials', 'ALTUSERBASE')
|
|
||||||
CAPTCHA_PATH = config.get('credentials', 'CAPTCHA_PATH')
|
CAPTCHA_PATH = config.get('credentials', 'CAPTCHA_PATH')
|
||||||
|
|
||||||
@app.route('/account/changepassword/', methods=['POST', 'GET'])
|
@app.route('/account/changepassword/', methods=['POST', 'GET'])
|
||||||
@@ -34,23 +33,22 @@ def changepassword():
|
|||||||
return 'Error: password is too short'
|
return 'Error: password is too short'
|
||||||
|
|
||||||
# Create a LUSER connection
|
# Create a LUSER connection
|
||||||
luser = LUSER(LDAPHOST,LDAPADMINNAME,LDAPPASS,USERBASE,ALTUSERBASE)
|
luser = LUSER(LDAPHOST,LDAPADMINNAME,LDAPPASS,USERBASE)
|
||||||
|
|
||||||
# Retrive current password
|
# Retrive current password
|
||||||
currentpassword = luser.getpassword(username)
|
currentpassword = luser.getpassword(username)
|
||||||
|
|
||||||
if currentpassword == False:
|
if currentpassword == False:
|
||||||
return 'User doesn't exist'
|
return "User doesn't exist"
|
||||||
|
|
||||||
if ldap_salted_sha1.verify(oldpassword, currentpassword) == False and oldpassword != LDAPPASS:
|
if ldap_salted_sha1.verify(oldpassword, currentpassword) == False and oldpassword != LDAPPASS:
|
||||||
return 'Wrong username/password combination'
|
return 'Wrong username/password combination'
|
||||||
|
|
||||||
ldaphash = ldap_salted_sha1.hash(newpassword)
|
ldaphash = ldap_salted_sha1.hash(newpassword)
|
||||||
althash = sha512_crypt.hash(newpassword)
|
|
||||||
|
|
||||||
# Try to change user password
|
# Try to change user password
|
||||||
try:
|
try:
|
||||||
if luser.changepassword(username, ldaphash, althash) == True:
|
if luser.changepassword(username, ldaphash) == True:
|
||||||
return 'User password successfuly changed'
|
return 'User password successfuly changed'
|
||||||
else:
|
else:
|
||||||
return 'User password change failed'
|
return 'User password change failed'
|
||||||
@@ -68,9 +66,9 @@ def unregister():
|
|||||||
password = request.form['password']
|
password = request.form['password']
|
||||||
|
|
||||||
# Create a LUSER connection
|
# 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'
|
return 'Wrong username/password combination'
|
||||||
|
|
||||||
# Try to delete user
|
# Try to delete user
|
||||||
@@ -141,14 +139,13 @@ def register():
|
|||||||
return 'Error: username can only contain letters and numbers'
|
return 'Error: username can only contain letters and numbers'
|
||||||
|
|
||||||
# Create a LUSER connection
|
# Create a LUSER connection
|
||||||
luser = LUSER(LDAPHOST,LDAPADMINNAME,LDAPPASS,USERBASE,ALTUSERBASE)
|
luser = LUSER(LDAPHOST,LDAPADMINNAME,LDAPPASS,USERBASE)
|
||||||
# Try to add user
|
# Try to add user
|
||||||
try:
|
try:
|
||||||
ldaphash = ldap_salted_sha1.hash(password)
|
ldaphash = ldap_salted_sha1.hash(password)
|
||||||
althash = sha512_crypt.hash(password)
|
|
||||||
#smtpctlout=subprocess.run(["smtpctl","encrypt", password],text=True,stdout=subprocess.PIPE)
|
#smtpctlout=subprocess.run(["smtpctl","encrypt", password],text=True,stdout=subprocess.PIPE)
|
||||||
#smtpdhash=smtpctlout.stdout[:-1]
|
#smtpdhash=smtpctlout.stdout[:-1]
|
||||||
if luser.add(username,ldaphash,althash):
|
if luser.add(username,ldaphash):
|
||||||
return 'User successfuly registered'
|
return 'User successfuly registered'
|
||||||
else:
|
else:
|
||||||
return 'User registration failed, username probably taken'
|
return 'User registration failed, username probably taken'
|
||||||
|
Reference in New Issue
Block a user