fix bugs
This commit is contained in:
		| @@ -1,10 +1,12 @@ | |||||||
| all: man deb  | all: man deb  | ||||||
|  |  | ||||||
| man: man/luser.1.md | man: man/luser.1.md | ||||||
|  | 	mkdir -p luser/usr/share/man/man1/ | ||||||
| 	pandoc man/luser.1.md -f markdown+hard_line_breaks -s -t man -o man/luser.1 | 	pandoc man/luser.1.md -f markdown+hard_line_breaks -s -t man -o man/luser.1 | ||||||
| 	cp man/luser.1 luser/usr/share/man/man1/ | 	cp man/luser.1 luser/usr/share/man/man1/ | ||||||
| 	gzip -f luser/usr/share/man/man1/luser.1 | 	gzip -f luser/usr/share/man/man1/luser.1 | ||||||
| deb: man ../requirments.txt ../run.py ../luser ../LICENSE | deb: man ../requirments.txt ../run.py ../luser ../LICENSE | ||||||
|  | 	mkdir -p luser/var/luser/luser | ||||||
| 	cp -r ../luser/* luser/var/luser/luser/ | 	cp -r ../luser/* luser/var/luser/luser/ | ||||||
| 	cp ../run.py luser/var/luser/ | 	cp ../run.py luser/var/luser/ | ||||||
| 	cp ../LICENSE luser/var/luser/ | 	cp ../LICENSE luser/var/luser/ | ||||||
| @@ -16,5 +18,3 @@ clean: | |||||||
| 	rm -f luser.deb | 	rm -f luser.deb | ||||||
| 	rm -f man/luser.1 | 	rm -f man/luser.1 | ||||||
| 	rm -rf luser/var | 	rm -rf luser/var | ||||||
| 	mkdir -p luser/var/luser/luser |  | ||||||
| 	mkdir -p luser/usr/share/man/man1/ |  | ||||||
|   | |||||||
| @@ -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.0.7 | Version: 1.0.8 | ||||||
|   | |||||||
							
								
								
									
										144
									
								
								luser/models.py
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								luser/models.py
									
									
									
									
									
								
							| @@ -1,3 +1,4 @@ | |||||||
|  | import ldap3 | ||||||
| from ldap3 import Server,Connection,ALL,MODIFY_REPLACE | from ldap3 import Server,Connection,ALL,MODIFY_REPLACE | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
|  |  | ||||||
| @@ -9,9 +10,30 @@ class LUSER(): | |||||||
|         admin_user := string DN of LDAP admin user |         admin_user := string DN of LDAP admin user | ||||||
|         admin_pass := string password of LDAP admin user |         admin_pass := string password of LDAP admin user | ||||||
|         base := string base in LDAP system where users are made |         base := string base in LDAP system where users are made | ||||||
|         basealt := string base in LDAP system where users are made with password hashes generated for openalt |  | ||||||
|     ''' |     ''' | ||||||
|  |  | ||||||
|  |     def getlastlog(self)->int: | ||||||
|  |         self.ldapconnection.search(search_base=f'uid=total,{self.logbase}',search_filter='(objectClass=person)', attributes=['uidNumber']) | ||||||
|  |         response = self.ldapconnection.response | ||||||
|  |         if response == []: | ||||||
|  |             response = 0 | ||||||
|  |         else: | ||||||
|  |             response = int(response['attributes']['uidNumber']) | ||||||
|  |         return response | ||||||
|  |  | ||||||
|  |     def setlastlog(self, newvalue: int): | ||||||
|  |         newvalue = int(newvalue) | ||||||
|  |  | ||||||
|  |         # Check if total record already present | ||||||
|  |         self.ldapconnection.search(search_base=f'uid=total,{self.logbase}',search_filter='(objectClass=person)', attributes=['uidNumber']) | ||||||
|  |         response = self.ldapconnection.response | ||||||
|  |   | ||||||
|  |         if response == []: | ||||||
|  |             self.ldapconnection.add(f'uid=total,{self.logbase}', OBJECTCLASSES, { 'uid' : 'total', 'uidNumber' : 0 }) | ||||||
|  |  | ||||||
|  |         self.ldapconnection.modify(f'uid=total,{self.logbase}', {'uidNumber' : (ldap3.MODIFY_REPLACE, [newvalue])}) | ||||||
|  |  | ||||||
|  |         return self.ldapconnection.response | ||||||
|  |  | ||||||
|     def findlastuid(self): |     def findlastuid(self): | ||||||
|         ''' |         ''' | ||||||
| @@ -33,16 +55,12 @@ class LUSER(): | |||||||
|  |  | ||||||
|         return max |         return max | ||||||
|  |  | ||||||
|     def expandbase(self, basealt = ''): |     def expandbase(self): | ||||||
|         ''' |         ''' | ||||||
|             Extract orgnaization, name of dc object and full domain part with all dc values from base |             Extract orgnaization, name of dc object and full domain part with all dc values from base | ||||||
|             basealt := string base in LDAP system where users are made, if not set the function uses base specified on creation of LUSER instance (self.base) |  | ||||||
|         ''' |         ''' | ||||||
|         # Split base string with commas to find values of organization and dc |         # Split base string with commas to find values of organization and dc | ||||||
|         if basealt == '': |         baselist = self.base.split(",") | ||||||
|             baselist = self.base.split(",") |  | ||||||
|         else: |  | ||||||
|             baselist = self.basealt.split(",") |  | ||||||
|          |          | ||||||
|         organization = '' |         organization = '' | ||||||
|         dc = '' |         dc = '' | ||||||
| @@ -73,15 +91,13 @@ class LUSER(): | |||||||
|   |   | ||||||
|         return organization, dc, dcfull, domain |         return organization, dc, dcfull, domain | ||||||
|  |  | ||||||
|     def __init__(self, ldap_host, admin_user, admin_pass, base, basealt='', autoconnect=True, lastUID = 1000): |     def __init__(self, ldap_host, admin_user, admin_pass, base, autoconnect=True, lastUID = 1000): | ||||||
|         self.ldap_host = ldap_host |         self.ldap_host = ldap_host | ||||||
|         self.admin_user = admin_user |         self.admin_user = admin_user | ||||||
|         self.admin_pass = admin_pass |         self.admin_pass = admin_pass | ||||||
|         self.base = base |         self.base = base | ||||||
|         self.basealt = basealt |  | ||||||
|         self.organization, self.dc, self.dcfull, self.domain = self.expandbase() |         self.organization, self.dc, self.dcfull, self.domain = self.expandbase() | ||||||
|         self.organizationalt, self.dcalt, self.dcfullalt, self.domainalt = self.expandbase(self.basealt) |         self.logbase = f'ou=log,{self.dcfull}' | ||||||
|         self.alt = True |  | ||||||
|         self.autoconnect = autoconnect |         self.autoconnect = autoconnect | ||||||
|         ldapserver = Server(ldap_host, use_ssl=True) |         ldapserver = Server(ldap_host, use_ssl=True) | ||||||
|         lastuidfound = 0 |         lastuidfound = 0 | ||||||
| @@ -92,6 +108,15 @@ class LUSER(): | |||||||
|         else: |         else: | ||||||
|             self.ldapconnection = Connection(ldapserver, admin_user, admin_pass, auto_bind=False) |             self.ldapconnection = Connection(ldapserver, admin_user, admin_pass, auto_bind=False) | ||||||
|  |  | ||||||
|  |         # Check if base and log base is created | ||||||
|  |         self.ldapconnection.search(search_base=f'{self.base}',search_filter='(objectClass=organizationalUnit)', attributes=['ou']) | ||||||
|  |         if self.ldapconnection.response == []: | ||||||
|  |             self.prepare() | ||||||
|  |  | ||||||
|  |         self.ldapconnection.search(search_base=f'{self.logbase}',search_filter='(objectClass=organizationalUnit)', attributes=['ou']) | ||||||
|  |         if self.ldapconnection.response == []: | ||||||
|  |             self.prepare() | ||||||
|  |  | ||||||
|         if lastuidfound == 0: |         if lastuidfound == 0: | ||||||
|             self.lastuid = lastUID |             self.lastuid = lastUID | ||||||
|             self.lastgid = lastUID |             self.lastgid = lastUID | ||||||
| @@ -99,31 +124,21 @@ class LUSER(): | |||||||
|             self.lastuid = lastuidfound  |             self.lastuid = lastuidfound  | ||||||
|             self.lastgid = lastuidfound   |             self.lastgid = lastuidfound   | ||||||
|  |  | ||||||
|  |  | ||||||
|         # Set alt boolean to false if basealt not set |  | ||||||
|         if basealt == '': |  | ||||||
|             self.alt = False |  | ||||||
|  |  | ||||||
|     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(f'dc={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}) | ||||||
|  |  | ||||||
|         # Add dcobject and organizational units as above for base alt |         # Create organizational units for log on LDAP server and store boolean indicating it's success | ||||||
|         rcode3 = True |         rcode3 = self.ldapconnection.add(self.logbase, ['top', 'organizationalUnit'], {'ou' : self.organization}) | ||||||
|         rcode4 = True |  | ||||||
|         if self.alt: |  | ||||||
|             rcode3 = self.ldapconnection.add(f'dc={self.dcfull}', ['dcObject', 'organization'], {'o' : self.dc, 'dc' : self.dc}) |  | ||||||
|             rcode4 = self.ldapconnection.add(self.base, ['top', 'organizationalUnit'], {'ou' : self.organization}) |  | ||||||
|  |  | ||||||
|         return rcode1 and rcode2 and rcode3 and rcode4 |         return rcode1 and rcode2 and rcode3 | ||||||
|  |  | ||||||
|     def lastpwchangenow(self): |     def lastpwchangenow(self): | ||||||
|         ''' |         ''' | ||||||
| @@ -133,12 +148,11 @@ class LUSER(): | |||||||
|  |  | ||||||
|         return str((datetime.utcnow() - datetime(1970,1,1)).days) |         return str((datetime.utcnow() - datetime(1970,1,1)).days) | ||||||
|  |  | ||||||
|     def add(self, user, password, althash=""): |     def add(self, user, password): | ||||||
|         ''' |         ''' | ||||||
|             Add a user to base in LDAP with user and pass as credentials |             Add a user to base in LDAP with user and pass as credentials | ||||||
|             user := string containing username |             user := string containing username | ||||||
|             password := string containing user password |             password := string containing user password | ||||||
|             althash := string containing user password/hash for the alternative base |  | ||||||
|         ''' |         ''' | ||||||
|  |  | ||||||
|         # Increase UID and GID counters |         # Increase UID and GID counters | ||||||
| @@ -154,16 +168,17 @@ class LUSER(): | |||||||
|         # Attributes for a user entry |         # Attributes for a user entry | ||||||
|         attributes = {'cn' : user, 'sn' : user, 'givenName' : user, 'uid' : user, 'uidNumber' : self.lastuid, 'gidNumber' : self.lastgid, 'homeDirectory' : f'/home/{user}', 'loginShell' : '/usr/bin/git-shell', 'gecos' : 'SystemUser', 'shadowLastChange' : self.lastpwchangenow(), 'shadowMax' : '45', 'userPassword' : password, 'mail' : f'{user}@{self.domain}' } |         attributes = {'cn' : user, 'sn' : user, 'givenName' : user, 'uid' : user, 'uidNumber' : self.lastuid, 'gidNumber' : self.lastgid, 'homeDirectory' : f'/home/{user}', 'loginShell' : '/usr/bin/git-shell', 'gecos' : 'SystemUser', 'shadowLastChange' : self.lastpwchangenow(), 'shadowMax' : '45', 'userPassword' : password, 'mail' : f'{user}@{self.domain}' } | ||||||
|  |  | ||||||
|         attributesalt = {'cn' : user, 'sn' : user, 'givenName' : user, 'uid' : user, 'uidNumber' : self.lastuid, 'gidNumber' : self.lastgid, 'homeDirectory' : f'/home/{user}', 'loginShell' : '/usr//bin/git-shell', 'gecos' : 'SystemUser', 'shadowLastChange' : self.lastpwchangenow(), 'shadowMax' : '45', 'userPassword' : althash, 'mail' : f'{user}@{self.domainalt}'} |  | ||||||
|  |  | ||||||
|         # Return boolean value of new user entry |         # Return boolean value of new user entry | ||||||
|         rcode1 = self.ldapconnection.add(f'{id},{self.base}', objectClass, attributes) |         rcode1 = self.ldapconnection.add(self.logbase, objectClass, attributes) | ||||||
|  |  | ||||||
|         # If alternative base is set add the same user to the alternative base as well, if not act as if it was success |         # Add new user to log | ||||||
|         if self.alt: |         attributes['description'] = 'ADD' | ||||||
|             rcode2 = self.ldapconnection.add(f'{id},{self.basealt}', objectClass, attributesalt) |         lastlog = self.getlastlog() + 1 | ||||||
|         else: |  | ||||||
|             rcode2 = True |         rcode2 = self.ldapconnection.add(f'uid={lastlog},{self.logbase}', objectClass, attributes) | ||||||
|  |  | ||||||
|  |         if rcode2: | ||||||
|  |             self.setlastlog(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 | ||||||
| @@ -174,29 +189,34 @@ class LUSER(): | |||||||
|  |  | ||||||
|             user := string containing username |             user := string containing username | ||||||
|             newpass := string containing new password |             newpass := string containing new password | ||||||
|             althash := string containing password/hash for alternative base |  | ||||||
|         ''' |         ''' | ||||||
|          |  | ||||||
|         # This variable holds boolean indicating successful change of user password |         # This variable holds boolean indicating successful change of user password | ||||||
|         chpassbool = self.ldapconnection.modify(f'uid={user},{self.base}', {'userPassword': (MODIFY_REPLACE,[newpass])}) |         chpassbool = False | ||||||
|  |  | ||||||
|         # If alternative base is set modify user password/hash with althash, if not, pretend change was successful |  | ||||||
|         if self.alt: |  | ||||||
|             chpassboolalt = self.ldapconnection.modify(f'uid={user},{self.basealt}', {'userPassword': (MODIFY_REPLACE,[althash])}) |  | ||||||
|         else: |  | ||||||
|             chpassboolalt = True |  | ||||||
|  |  | ||||||
|         # This variable holds boolean indicating successful change of shadowLastChange value to current time |         # This variable holds boolean indicating successful change of shadowLastChange value to current time | ||||||
|         chlastchangebool = self.ldapconnection.modify(f'uid={user},{self.base}', {'shadowLastChange' : (MODIFY_REPLACE,[self.lastpwchangenow()])}) |         chlastchangebool = False | ||||||
|  |  | ||||||
|         # If alternative base is set modify user password/hash with althash, if not, pretend change was successful |         USERATTRIBUTES=['cn' , 'sn', 'givenName', 'uid', 'uidNumber' , 'gidNumber', 'homeDirectory', 'loginShell', 'gecos' , 'shadowLastChange', 'shadowMax', 'userPassword', 'mail'] | ||||||
|         if self.alt: |  | ||||||
|             chlastchangeboolalt = self.ldapconnection.modify(f'uid={user},{self.base}', {'shadowLastChange' : (MODIFY_REPLACE, [self.lastpwchangenow()])}) |         OBJECTCLASSES = ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount', 'shadowAccount'] | ||||||
|         else: |  | ||||||
|             chlastchangeboolalt = True |         self.ldapconnection.search(search_base=f'uid=username,{self.base}',search_filter='(objectClass=person)', attributes=USERATTRIBUTES) | ||||||
|  |  | ||||||
|  |         userdata = self.ldapconnection.response[0] | ||||||
|  |         userdata['attributes']['description'] = 'CHANGEPASS' | ||||||
|  |  | ||||||
|  |         lastlog = self.getlastlog() + 1 | ||||||
|  |  | ||||||
|  |         rcode1 = self.ldapconnection.add(f'uid={lastlog},{self.logbase}', OBJECTCLASSES, userdata['attributes']) | ||||||
|  |  | ||||||
|  |         if rcode1: | ||||||
|  |             self.setlastlog(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()])}) | ||||||
|  |  | ||||||
|         # Return True only if changing of both password and time of last password change was successful |         # Return True only if changing of both password and time of last password change was successful | ||||||
|         return chpassbool and chpassboolalt and chlastchangebool and chlastchangeboolalt |         return chpassbool and chlastchangebool | ||||||
|  |  | ||||||
|     def delete(self, user): |     def delete(self, user): | ||||||
|         ''' |         ''' | ||||||
| @@ -205,15 +225,23 @@ class LUSER(): | |||||||
|            user := string containing username |            user := string containing username | ||||||
|         ''' |         ''' | ||||||
|  |  | ||||||
|         rcode1 = self.ldapconnection.delete(f'uid={user},{self.base}') |         USERATTRIBUTES=['cn' , 'sn', 'givenName', 'uid', 'uidNumber' , 'gidNumber', 'homeDirectory', 'loginShell', 'gecos' , 'shadowLastChange', 'shadowMax', 'userPassword', 'mail'] | ||||||
|  |  | ||||||
|         # If alternative base is set delete user from alternative base, if not, pretend deletion was successful |         OBJECTCLASSES = ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount', 'shadowAccount'] | ||||||
|         if self.alt: |  | ||||||
|             rcode2 = self.ldapconnection.delete(f'uid={user},{self.basealt}') |         self.ldapconnection.search(search_base=f'uid={user},{self.base}',search_filter='(objectClass=person)', attributes=USERATTRIBUTES) | ||||||
|         else: |  | ||||||
|             rcode2 = True |         userdata = self.ldapconnection.response[0] | ||||||
|  |         userdata['attributes']['description'] = 'DELETE' | ||||||
|  |  | ||||||
|  |         lastlog = self.getlastlog() + 1 | ||||||
|  |  | ||||||
|  |         rcode1 = self.ldapconnection.add(f'uid={lastlog},{self.logbase}', OBJECTCLASSES, userdata['attributes']) | ||||||
|  |  | ||||||
|  |         if rcode1: | ||||||
|  |             self.setlastlog(lastlog) | ||||||
|  |             rcode2 = self.ldapconnection.delete(f'uid={user},{self.base}') | ||||||
|  |  | ||||||
|         # Return True only if deletion from both bases was successful |  | ||||||
|         return rcode1 and rcode2 |         return rcode1 and rcode2 | ||||||
|  |  | ||||||
|     def getpassword(self, user): |     def getpassword(self, user): | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user