diff --git a/flaschengeist/modules/auth_ldap/__init__.py b/flaschengeist/modules/auth_ldap/__init__.py index b69f2c9..7da6d10 100644 --- a/flaschengeist/modules/auth_ldap/__init__.py +++ b/flaschengeist/modules/auth_ldap/__init__.py @@ -1,20 +1,193 @@ import flaschengeist.modules as modules +from flaschengeist import logger from flask import current_app as app +from flask_ldapconn import LDAPConn +from ldap3 import SUBTREE#, MODIFY_REPLACE, HASHED_SALTED_MD5 +import ssl + +class AuthLDAP(modules.Auth): + _default = { + 'PORT': '389', + 'USE_SSL': 'False' + } -class AuthPlain(modules.Auth): def configure(self, config): + for name in self._default: + if name not in config: + config[name] = self._default[name] + app.config.update( - 'LDAP_SERVER' = config['URL'], - 'LDAP_PORT' = config['PORT'], - 'LDAP_BINDDN' = config['BINDDN'], - 'LDAP_SECRET' = config['SECRET'] - 'LDAP_USE_SSL' = config['USE_SSL'], - 'LDAP_TLS_VERSION' = ssl.PROTOCOL_TLSv1_2, - 'LDAP_REQUIRE_CERT' = ssl.CERT_NONE, - 'FORCE_ATTRIBUTE_VALUE_AS_LIST' = True + LDAP_SERVER = config['URL'], + LDAP_PORT = config.getint('PORT'), + LDAP_BINDDN = config['BINDDN'], + LDAP_USE_TLS = False, + LDAP_USE_SSL = config.getboolean('USE_SSL'), + LDAP_TLS_VERSION = ssl.PROTOCOL_TLSv1_2, + LDAP_REQUIRE_CERT = ssl.CERT_NONE, + FORCE_ATTRIBUTE_VALUE_AS_LIST = True ) + if 'SECRET' in config: + app.config['LDAP_SECRET'] = config['SECRET'], + + self.ldap = LDAPConn(app) + self.dn = config['BASEDN'] def login(self, user, password): if not user: return False + try: + r = self.ldap.authenticate(user.uid, password, 'uid', self.dn) + return r == True + except Exception as err: + logger.warning("Exception while login into ldap", exc_info=True) return False + + def modifyUser(self, user): + try: + ldap_conn = self.ldap.bind(user.uid, password) + if attributes: + if 'username' in attributes: + debug.debug("change username, so change first in database") + db.changeUsername(user, attributes['username']) + ldap.modifyUser(user, ldap_conn, attributes) + if 'username' in attributes: + retVal = self.getUser(attributes['username']) + debug.debug("user is {{ {} }}".format(retVal)) + return retVal + else: + retVal = self.getUser(user.uid) + debug.debug("user is {{ {} }}".format(retVal)) + return retVal + return self.getUser(user.uid) + + except UsernameExistLDAP as err: + debug.debug( + "username exists on ldap, rechange username on database", exc_info=True) + db.changeUsername(user, user.uid) + raise Exception(err) + except LDAPExcetpion as err: + if 'username' in attributes: + db.changeUsername(user, user.uid) + raise Exception(err) + except LDAPPasswordIsMandatoryError as err: + raise Exception('Password wurde nicht gesetzt!!') + except LDAPBindError as err: + raise Exception('Password ist falsch') + except Exception as err: + raise Exception(err) + debug.info("modify ldap data from user {{ {} }} with attributes (can't show because here can be a password)".format(user)) + try: + if 'username' in attributes: + debug.debug("change username") + conn.search('ou=user,{}'.format(self.dn), '(uid={})'.format(attributes['username'])) + if conn.entries: + debug.warning("username already exists", exc_info=True) + raise UsernameExistLDAP("Username already exists in LDAP") + #create modifyer + mody = {} + if 'username' in attributes: + mody['uid'] = [(MODIFY_REPLACE, [attributes['username']])] + if 'firstname' in attributes: + mody['givenName'] = [(MODIFY_REPLACE, [attributes['firstname']])] + if 'lastname' in attributes: + mody['sn'] = [(MODIFY_REPLACE, [attributes['lastname']])] + if 'mail' in attributes: + mody['mail'] = [(MODIFY_REPLACE, [attributes['mail']])] + if 'password' in attributes: + salted_password = hashed(HASHED_SALTED_MD5, attributes['password']) + mody['userPassword'] = [(MODIFY_REPLACE, [salted_password])] + debug.debug("modyfier are (can't show because here can be a password)") + conn.modify(user.dn, mody) + except Exception as err: + debug.warning("exception in modify user data from ldap", exc_info=True) + raise LDAPExcetpion("Something went wrong in LDAP: {}".format(err)) + + def updateUser(self, user): + self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid={})'.format(user.uid), SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail']) + r = self.ldap.connection.response[0]['attributes'] + if r['uid'][0] == user.uid: + user.setAttribute('DN', self.ldap.connection.response[0]['dn']) + user.firstname = r['givenName'][0] + user.lastname = r['sn'][0] + if r['mail']: + user.mail = r['mail'][0] + if 'displayName' in r: + user.displayname = r['displayName'][0] + for group in self._getGroups(user.uid): + user.addGroup(group) + + def _getGroups(self, uid): + try: + groups = [] + + self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid={})'.format(uid), SUBTREE, attributes=['gidNumber']) + main_group_number = self.ldap.connection.response[0]['attributes']['gidNumber'] + if main_group_number: + if type(main_group_number) is list: + main_group_number = main_group_number[0] + self.ldap.connection.search('ou=group,{}'.format(self.dn), '(gidNumber={})'.format(main_group_number), attributes=['cn']) + groups.append(self.ldap.connection.response[0]['attributes']['cn'][0]) + + self.ldap.connection.search('ou=group,{}'.format(self.dn), '(memberUID={})'.format(uid), SUBTREE, attributes=['cn']) + groups_data = self.ldap.connection.response + for data in groups_data: + groups.append(data['attributes']['cn'][0]) + return groups + except Exception as err: + debug.warning("exception in get groups from ldap", exc_info=True) + return [] + +# def getAllUser(self): +# debug.info("get all users from ldap") +# retVal = [] +# self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid=*)', SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail']) +# data = self.ldap.connection.response +# debug.debug("data is {{ {} }}".format(data)) +# for user in data: +# if 'uid' in user['attributes']: +# username = user['attributes']['uid'][0] +# firstname = user['attributes']['givenName'][0] +# lastname = user['attributes']['sn'][0] +# retVal.append({'username': username, 'firstname': firstname, 'lastname': lastname}) +# debug.debug("users are {{ {} }}".format(retVal)) +# return retVal + +# def searchUser(self, searchString): +# +# name = searchString.split(" ") +# +# for i in range(len(name)): +# name[i] = "*"+name[i]+"*" +# +# +# print(name) +# +# name_result = [] +# +# if len(name) == 1: +# if name[0] == "**": +# self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid=*)', SUBTREE, +# attributes=['uid', 'givenName', 'sn']) +# name_result.append(self.ldap.connection.response) +# else: +# self.ldap.connection.search('ou=user,{}'.format(self.dn), '(givenName={})'.format(name[0]), SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail']) +# name_result.append(self.ldap.connection.response) +# self.ldap.connection.search('ou=user,{}'.format(self.dn), '(sn={})'.format(name[0]), SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail']) +# name_result.append(self.ldap.connection.response) +# else: +# self.ldap.connection.search('ou=user,{}'.format(self.dn), '(givenName={})'.format(name[1]), SUBTREE, attributes=['uid', 'givenName', 'sn']) +# name_result.append(self.ldap.connection.response) +# self.ldap.connection.search('ou=user,{}'.format(self.dn), '(sn={})'.format(name[1]), SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail']) +# name_result.append(self.ldap.connection.response) +# retVal = [] + +# for names in name_result: +# for user in names: +# if 'uid' in user['attributes']: +# username = user['attributes']['uid'][0] +# if not self.__isUserInList(retVal, username): +# firstname = user['attributes']['givenName'][0] +# lastname = user['attributes']['sn'][0] +# retVal.append({'username': username, 'firstname': firstname, 'lastname': lastname}) +# +# return retVal diff --git a/flaschengeist/system/controller/ldapController.py b/flaschengeist/system/controller/ldapController.py deleted file mode 100644 index e17d3e7..0000000 --- a/flaschengeist/system/controller/ldapController.py +++ /dev/null @@ -1,204 +0,0 @@ -from geruecht import ldap -from ldap3 import SUBTREE, MODIFY_REPLACE, HASHED_SALTED_MD5 -from ldap3.utils.hashed import hashed -from geruecht.model import MONEY, USER, GASTRO, BAR, VORSTAND, EXTERN -from geruecht.exceptions import PermissionDenied -from . import Singleton -from geruecht.exceptions import UsernameExistLDAP, LDAPExcetpion -from . import ldapConfig -from geruecht.logger import getDebugLogger - -debug = getDebugLogger() - -class LDAPController(metaclass=Singleton): - ''' - Authentification over LDAP. Create Account on-the-fly - ''' - - def __init__(self): - debug.info("init ldap controller") - self.dn = ldapConfig['DN'] - self.ldap = ldap - debug.debug("base dn is {{ {} }}".format(self.dn)) - debug.debug("ldap is {{ {} }}".format(self.ldap)) - - - def login(self, username, password): - debug.info("login user {{ {} }} in ldap") - try: - retVal = self.ldap.authenticate(username, password, 'uid', self.dn) - debug.debug("authentification to ldap is {{ {} }}".format(retVal)) - if not retVal: - debug.debug("authenification is incorrect") - raise PermissionDenied("Invalid Password or Username") - except Exception as err: - debug.warning("exception while login into ldap", exc_info=True) - raise PermissionDenied("Invalid Password or Username. {}".format(err)) - - def bind(self, user, password): - debug.info("bind user {{ {} }} to ldap") - ldap_conn = self.ldap.connect(user.dn, password) - debug.debug("ldap_conn is {{ {} }}".format(ldap_conn)) - return ldap_conn - - def getUserData(self, username): - debug.info("get user data from ldap of user {{ {} }}".format(username)) - try: - debug.debug("search user in ldap") - self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid={})'.format(username), SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail']) - user = self.ldap.connection.response[0]['attributes'] - debug.debug("user is {{ {} }}".format(user)) - retVal = { - 'dn': self.ldap.connection.response[0]['dn'], - 'firstname': user['givenName'][0], - 'lastname': user['sn'][0], - 'uid': user['uid'][0], - } - if user['mail']: - retVal['mail'] = user['mail'][0] - debug.debug("user is {{ {} }}".format(retVal)) - if retVal['uid'] == username: - return retVal - else: - raise Exception() - except: - debug.warning("exception in get user data from ldap", exc_info=True) - raise PermissionDenied("No User exists with this uid.") - - - def getGroup(self, username): - debug.info("get group from user {{ {} }}".format(username)) - try: - retVal = [] - self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid={})'.format(username), SUBTREE, attributes=['gidNumber']) - main_group_number = self.ldap.connection.response[0]['attributes']['gidNumber'] - debug.debug("main group number is {{ {} }}".format(main_group_number)) - if main_group_number: - if type(main_group_number) is list: - main_group_number = main_group_number[0] - self.ldap.connection.search('ou=group,{}'.format(self.dn), '(gidNumber={})'.format(main_group_number), attributes=['cn']) - group_name = self.ldap.connection.response[0]['attributes']['cn'][0] - debug.debug("group name is {{ {} }}".format(group_name)) - if group_name == 'ldap-user': - retVal.append(USER) - if group_name == 'extern': - retVal.append(EXTERN) - - self.ldap.connection.search('ou=group,{}'.format(self.dn), '(memberUID={})'.format(username), SUBTREE, attributes=['cn']) - groups_data = self.ldap.connection.response - debug.debug("groups number is {{ {} }}".format(groups_data)) - for data in groups_data: - group_name = data['attributes']['cn'][0] - debug.debug("group name is {{ {} }}".format(group_name)) - if group_name == 'finanzer': - retVal.append(MONEY) - elif group_name == 'gastro': - retVal.append(GASTRO) - elif group_name == 'bar': - retVal.append(BAR) - elif group_name == 'vorstand': - retVal.append(VORSTAND) - elif group_name == 'ldap-user': - retVal.append(USER) - debug.debug("groups are {{ {} }}".format(retVal)) - return retVal - except Exception as err: - debug.warning("exception in get groups from ldap", exc_info=True) - raise LDAPExcetpion(str(err)) - - def __isUserInList(self, list, username): - help_list = [] - for user in list: - help_list.append(user['username']) - if username in help_list: - return True - return False - - def getAllUser(self): - debug.info("get all users from ldap") - retVal = [] - self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid=*)', SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail']) - data = self.ldap.connection.response - debug.debug("data is {{ {} }}".format(data)) - for user in data: - if 'uid' in user['attributes']: - username = user['attributes']['uid'][0] - firstname = user['attributes']['givenName'][0] - lastname = user['attributes']['sn'][0] - retVal.append({'username': username, 'firstname': firstname, 'lastname': lastname}) - debug.debug("users are {{ {} }}".format(retVal)) - return retVal - - def searchUser(self, searchString): - - name = searchString.split(" ") - - for i in range(len(name)): - name[i] = "*"+name[i]+"*" - - - print(name) - - name_result = [] - - if len(name) == 1: - if name[0] == "**": - self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid=*)', SUBTREE, - attributes=['uid', 'givenName', 'sn']) - name_result.append(self.ldap.connection.response) - else: - self.ldap.connection.search('ou=user,{}'.format(self.dn), '(givenName={})'.format(name[0]), SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail']) - name_result.append(self.ldap.connection.response) - self.ldap.connection.search('ou=user,{}'.format(self.dn), '(sn={})'.format(name[0]), SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail']) - name_result.append(self.ldap.connection.response) - else: - self.ldap.connection.search('ou=user,{}'.format(self.dn), '(givenName={})'.format(name[1]), SUBTREE, attributes=['uid', 'givenName', 'sn']) - name_result.append(self.ldap.connection.response) - self.ldap.connection.search('ou=user,{}'.format(self.dn), '(sn={})'.format(name[1]), SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail']) - name_result.append(self.ldap.connection.response) - retVal = [] - - for names in name_result: - for user in names: - if 'uid' in user['attributes']: - username = user['attributes']['uid'][0] - if not self.__isUserInList(retVal, username): - firstname = user['attributes']['givenName'][0] - lastname = user['attributes']['sn'][0] - retVal.append({'username': username, 'firstname': firstname, 'lastname': lastname}) - - return retVal - - def modifyUser(self, user, conn, attributes): - debug.info("modify ldap data from user {{ {} }} with attributes (can't show because here can be a password)".format(user)) - try: - if 'username' in attributes: - debug.debug("change username") - conn.search('ou=user,{}'.format(self.dn), '(uid={})'.format(attributes['username'])) - if conn.entries: - debug.warning("username already exists", exc_info=True) - raise UsernameExistLDAP("Username already exists in LDAP") - #create modifyer - mody = {} - if 'username' in attributes: - mody['uid'] = [(MODIFY_REPLACE, [attributes['username']])] - if 'firstname' in attributes: - mody['givenName'] = [(MODIFY_REPLACE, [attributes['firstname']])] - if 'lastname' in attributes: - mody['sn'] = [(MODIFY_REPLACE, [attributes['lastname']])] - if 'mail' in attributes: - mody['mail'] = [(MODIFY_REPLACE, [attributes['mail']])] - if 'password' in attributes: - salted_password = hashed(HASHED_SALTED_MD5, attributes['password']) - mody['userPassword'] = [(MODIFY_REPLACE, [salted_password])] - debug.debug("modyfier are (can't show because here can be a password)") - conn.modify(user.dn, mody) - except Exception as err: - debug.warning("exception in modify user data from ldap", exc_info=True) - raise LDAPExcetpion("Something went wrong in LDAP: {}".format(err)) - - - -if __name__ == '__main__': - a = LDAPController() - a.getUserData('jhille') diff --git a/setup.py b/setup.py index 83cff6d..9409bc6 100644 --- a/setup.py +++ b/setup.py @@ -10,14 +10,20 @@ setup( packages=find_packages(), package_data={'': ['*.yml']}, scripts=['run_flaschengeist'], - install_requires=['Flask >= 1.1', 'PyYAML>=5.3.1', 'sqlalchemy>=1.3', "flask_sqlalchemy", "flask_ldapconn", "flask_cors"], + install_requires=['Flask >= 1.1', 'PyYAML>=5.3.1', 'sqlalchemy>=1.3', "flask_sqlalchemy", "flask_cors"], + extras_require={ + 'ldap': [ + 'flask_ldapconn', + 'ldap3' + ] + }, entry_points = { 'flaschengeist.plugin': [ 'auth = flaschengeist.modules.auth:register' ], 'flaschengeist.auth': [ 'auth_plain = flaschengeist.modules.auth_plain:AuthPlain', - 'auth_ldap = flaschengeist.modules.auth_ldap:AuthLDAP' + 'auth_ldap = flaschengeist.modules.auth_ldap:AuthLDAP [ldap]' ] } )