Merge branch 'feature/ldap' into develop

This commit is contained in:
Tim Gröger 2020-01-27 20:18:24 +01:00
commit 0ab9da4736
12 changed files with 217 additions and 78 deletions

View File

@ -7,6 +7,7 @@
from .logger import getLogger from .logger import getLogger
from geruecht.controller import dbConfig from geruecht.controller import dbConfig
from flask_mysqldb import MySQL from flask_mysqldb import MySQL
from flask_ldapconn import LDAPConn
LOGGER = getLogger(__name__) LOGGER = getLogger(__name__)
LOGGER.info("Initialize App") LOGGER.info("Initialize App")
@ -23,6 +24,13 @@ app.config['MYSQL_USER'] = dbConfig['user']
app.config['MYSQL_PASSWORD'] = dbConfig['passwd'] app.config['MYSQL_PASSWORD'] = dbConfig['passwd']
app.config['MYSQL_DB'] = dbConfig['database'] app.config['MYSQL_DB'] = dbConfig['database']
app.config['MYSQL_CURSORCLASS'] = 'DictCursor' app.config['MYSQL_CURSORCLASS'] = 'DictCursor'
app.config['LDAP_SERVER'] = '192.168.5.128'
app.config['LDAP_PORT'] = 389
app.config['LDAP_BINDDN'] = 'dc=ldap,dc=example,dc=local'
app.config['LDAP_USE_TLS'] = False
app.config['FORCE_ATTRIBUTE_VALUE_AS_LIST'] = True
ldap = LDAPConn(app)
db = MySQL(app) db = MySQL(app)
from geruecht import routes from geruecht import routes

View File

@ -70,7 +70,7 @@ class AccesTokenController(metaclass=Singleton):
LOGGER.info("Found no valid AccessToken with token: {} and group: {}".format(token, group)) LOGGER.info("Found no valid AccessToken with token: {} and group: {}".format(token, group))
return False return False
def createAccesToken(self, user): def createAccesToken(self, user, ldap_conn):
""" Create an AccessToken """ Create an AccessToken
Create an AccessToken for an User and add it to the tokenList. Create an AccessToken for an User and add it to the tokenList.
@ -85,7 +85,7 @@ class AccesTokenController(metaclass=Singleton):
now = datetime.ctime(datetime.now()) now = datetime.ctime(datetime.now())
token = hashlib.md5((now + user.dn).encode('utf-8')).hexdigest() token = hashlib.md5((now + user.dn).encode('utf-8')).hexdigest()
self.checkBar(user) self.checkBar(user)
accToken = AccessToken(user, token, datetime.now()) accToken = AccessToken(user, token, ldap_conn, datetime.now())
LOGGER.debug("Add AccessToken {} to current Tokens".format(accToken)) LOGGER.debug("Add AccessToken {} to current Tokens".format(accToken))
self.tokenList.append(accToken) self.tokenList.append(accToken)
LOGGER.info("Finished create AccessToken {} with Token {}".format(accToken, token)) LOGGER.info("Finished create AccessToken {} with Token {}".format(accToken, token))

View File

@ -4,6 +4,8 @@ from geruecht import db
from geruecht.model.user import User from geruecht.model.user import User
from geruecht.model.creditList import CreditList from geruecht.model.creditList import CreditList
from datetime import datetime, timedelta from datetime import datetime, timedelta
from geruecht.exceptions import UsernameExistDB, DatabaseExecption
import traceback
class DatabaseController(metaclass=Singleton): class DatabaseController(metaclass=Singleton):
''' '''
@ -54,10 +56,12 @@ class DatabaseController(metaclass=Singleton):
def _convertGroupToString(self, groups): def _convertGroupToString(self, groups):
retVal = '' retVal = ''
for group in groups: print('groups: {}'.format(groups))
if len(retVal) != 0: if groups:
retVal += ',' for group in groups:
retVal += group if len(retVal) != 0:
retVal += ','
retVal += group
return retVal return retVal
@ -71,6 +75,7 @@ class DatabaseController(metaclass=Singleton):
def updateUser(self, user): def updateUser(self, user):
cursor = self.db.connection.cursor() cursor = self.db.connection.cursor()
print('uid: {}; group: {}'.format(user.uid, user.group))
groups = self._convertGroupToString(user.group) groups = self._convertGroupToString(user.group)
sql = "update user set dn='{}', firstname='{}', lastname='{}', gruppe='{}', lockLimit={}, locked={}, autoLock={}, mail='{}' where uid='{}'".format( sql = "update user set dn='{}', firstname='{}', lastname='{}', gruppe='{}', lockLimit={}, locked={}, autoLock={}, mail='{}' where uid='{}'".format(
user.dn, user.firstname, user.lastname, groups, user.limit, user.locked, user.autoLock, user.mail, user.uid) user.dn, user.firstname, user.lastname, groups, user.limit, user.locked, user.autoLock, user.mail, user.uid)
@ -144,9 +149,26 @@ class DatabaseController(metaclass=Singleton):
def deleteWorker(self, user, date): def deleteWorker(self, user, date):
cursor = self.db.connection.cursor() try:
cursor.execute("delete from bardienste where user_id={} and startdatetime='{}'".format(user.id, date)) cursor = self.db.connection.cursor()
self.db.connection.commit() cursor.execute("delete from bardienste where user_id={} and startdatetime='{}'".format(user.id, date))
self.db.connection.commit()
except Exception as err:
traceback.print_exc()
def changeUsername(self, user, newUsername):
try:
cursor= self.db.connection.cursor()
cursor.execute("select * from user where uid='{}'".format(newUsername))
data = cursor.fetchall()
if data:
raise UsernameExistDB("Username already exists")
else:
cursor.execute("update user set uid='{}' where id={}".format(newUsername, user.id))
self.db.connection()
except Exception as err:
traceback.print_exc()
raise DatabaseExecption("Something went worng with Datatabase: {}".format(err))
if __name__ == '__main__': if __name__ == '__main__':
db = DatabaseController() db = DatabaseController()

View File

@ -1,72 +1,75 @@
import ldap 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 from geruecht.model import MONEY, USER, GASTRO, BAR
from geruecht.exceptions import PermissionDenied from geruecht.exceptions import PermissionDenied
from . import Singleton from . import Singleton
from geruecht.exceptions import UsernameExistLDAP, LDAPExcetpion
import traceback
class LDAPController(metaclass=Singleton): class LDAPController(metaclass=Singleton):
''' '''
Authentification over LDAP. Create Account on-the-fly Authentification over LDAP. Create Account on-the-fly
''' '''
def __init__(self, url="ldap://192.168.5.108", dn='dc=ldap,dc=example,dc=local'): def __init__(self, dn='dc=ldap,dc=example,dc=local'):
self.url = url
self.dn = dn self.dn = dn
self.connect() self.ldap = ldap
def connect(self):
try:
self.client = ldap.initialize(self.url, bytes_mode=False)
except Exception as err:
raise err
def login(self, username, password): def login(self, username, password):
self.connect()
try: try:
cn = self.client.search_s("ou=user,{}".format(self.dn), ldap.SCOPE_SUBTREE, 'uid={}'.format(username),['cn'])[0][1]['cn'][0].decode('utf-8') retVal = self.ldap.authenticate(username, password, 'uid', self.dn)
self.client.bind_s("cn={},ou=user,{}".format(cn, self.dn), password) if not retVal:
self.client.unbind_s() raise PermissionDenied("Invalid Password or Username")
except: except Exception as err:
self.client.unbind_s() traceback.print_exception(err)
raise PermissionDenied("Invalid Password or Username") raise PermissionDenied("Wrong username or password.")
def bind(self, user, password):
ldap_conn = self.ldap.connect(user.dn, password)
return ldap_conn
def getUserData(self, username): def getUserData(self, username):
try: try:
self.connect() self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid={})'.format(username), SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail'])
search_data = self.client.search_s('ou=user,{}'.format(self.dn), ldap.SCOPE_SUBTREE, 'uid={}'.format(username), ['uid', 'givenName', 'sn', 'mail']) user = self.ldap.connection.response[0]['attributes']
retVal = search_data[0][1] retVal = {
for k,v in retVal.items(): 'dn': self.ldap.connection.response[0]['dn'],
retVal[k] = v[0].decode('utf-8') 'firstname': user['givenName'][0],
retVal['dn'] = self.dn 'lastname': user['sn'][0],
retVal['firstname'] = retVal['givenName'] 'uid': username
retVal['lastname'] = retVal['sn'] }
return retVal return retVal
except: except:
raise PermissionDenied("No User exists with this uid.") raise PermissionDenied("No User exists with this uid.")
def getGroup(self, username): def getGroup(self, username):
retVal = [] try:
self.connect() retVal = []
main_group_data = self.client.search_s('ou=user,{}'.format(self.dn), ldap.SCOPE_SUBTREE, 'uid={}'.format(username), ['gidNumber']) self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid={})'.format(username), SUBTREE, attributes=['gidNumber'])
if main_group_data: response = self.ldap.connection.response
main_group_number = main_group_data[0][1]['gidNumber'][0].decode('utf-8') main_group_number = self.ldap.connection.response[0]['attributes']['gidNumber']
group_data = self.client.search_s('ou=group,{}'.format(self.dn), ldap.SCOPE_SUBTREE, 'gidNumber={}'.format(main_group_number), ['cn']) if main_group_number:
if group_data: group_data = self.ldap.connection.search('ou=group,{}'.format(self.dn), '(gidNumber={})'.format(main_group_number), attributes=['cn'])
group_name = group_data[0][1]['cn'][0].decode('utf-8') group_name = self.ldap.connection.response[0]['attributes']['cn'][0]
if group_name == 'ldap-user': if group_name == 'ldap-user':
retVal.append(USER) retVal.append(USER)
groups_data = self.client.search_s('ou=group,{}'.format(self.dn), ldap.SCOPE_SUBTREE, 'memberUID={}'.format(username), ['cn']) self.ldap.connection.search('ou=group,{}'.format(self.dn), '(memberUID={})'.format(username), SUBTREE, attributes=['cn'])
for data in groups_data: groups_data = self.ldap.connection.response
print(data[1]['cn'][0].decode('utf-8')) for data in groups_data:
group_name = data[1]['cn'][0].decode('utf-8') group_name = data['attributes']['cn'][0]
if group_name == 'finanzer': if group_name == 'finanzer':
retVal.append(MONEY) retVal.append(MONEY)
elif group_name == 'gastro': elif group_name == 'gastro':
retVal.append(GASTRO) retVal.append(GASTRO)
elif group_name == 'bar': elif group_name == 'bar':
retVal.append(BAR) retVal.append(BAR)
return retVal return retVal
except Exception as err:
traceback.print_exc()
def __isUserInList(self, list, username): def __isUserInList(self, list, username):
help_list = [] help_list = []
@ -77,19 +80,19 @@ class LDAPController(metaclass=Singleton):
return False return False
def getAllUser(self): def getAllUser(self):
self.connect()
retVal = [] retVal = []
data = self.client.search_s('ou=user,{}'.format(self.dn), ldap.SCOPE_SUBTREE, attrlist=['uid', 'givenName', 'sn', 'mail']) self.ldap.connection.search()
self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid=*)', SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail'])
data = self.ldap.connection.response
for user in data: for user in data:
if 'uid' in user[1]: if 'uid' in user['attributes']:
username = user[1]['uid'][0].decode('utf-8') username = user['attributes']['uid'][0]
firstname = user[1]['givenName'][0].decode('utf-8') firstname = user['attributes']['givenName'][0]
lastname = user[1]['sn'][0].decode('utf-8') lastname = user['attributes']['sn'][0]
retVal.append({'username': username, 'firstname': firstname, 'lastname': lastname}) retVal.append({'username': username, 'firstname': firstname, 'lastname': lastname})
return retVal return retVal
def searchUser(self, searchString): def searchUser(self, searchString):
self.connect()
name = searchString.split(" ") name = searchString.split(" ")
@ -103,29 +106,57 @@ class LDAPController(metaclass=Singleton):
if len(name) == 1: if len(name) == 1:
if name[0] == "**": if name[0] == "**":
name_result.append(self.client.search_s('ou=user,{}'.format(self.dn), ldap.SCOPE_SUBTREE, self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid=*)', SUBTREE,
attrlist=['uid', 'givenName', 'sn'])) attributes=['uid', 'givenName', 'sn'])
name_result.append(self.ldap.connection.response)
else: else:
name_result.append(self.client.search_s('ou=user,{}'.format(self.dn), ldap.SCOPE_SUBTREE, 'givenName={}'.format(name[0]), ['uid', 'givenName', 'sn', 'mail'])) self.ldap.connection.search('ou=user,{}'.format(self.dn), '(givenName={})'.format(name[0]), SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail'])
name_result.append(self.client.search_s('ou=user,{}'.format(self.dn), ldap.SCOPE_SUBTREE, 'sn={}'.format(name[0]),['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: else:
name_result.append(self.client.search_s('ou=user,{}'.format(self.dn), ldap.SCOPE_SUBTREE, self.ldap.connection.search('ou=user,{}'.format(self.dn), '(givenName={})'.format(name[1]), SUBTREE, attributes=['uid', 'givenName', 'sn'])
'givenName={}'.format(name[1]), ['uid', 'givenName', 'sn'])) name_result.append(self.ldap.connection.response)
name_result.append(self.client.search_s('ou=user,{}'.format(self.dn), ldap.SCOPE_SUBTREE, 'sn={}'.format(name[1]), self.ldap.connection.search('ou=user,{}'.format(self.dn), '(sn={})'.format(name[1]), SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail'])
['uid', 'givenName', 'sn', 'mail'])) name_result.append(self.ldap.connection.response)
retVal = [] retVal = []
for names in name_result: for names in name_result:
for user in names: for user in names:
if 'uid' in user[1]: if 'uid' in user['attributes']:
username = user[1]['uid'][0].decode('utf-8') username = user['attributes']['uid'][0]
if not self.__isUserInList(retVal, username): if not self.__isUserInList(retVal, username):
firstname = user[1]['givenName'][0].decode('utf-8') firstname = user['attributes']['givenName'][0]
lastname = user[1]['sn'][0].decode('utf-8') lastname = user['attributes']['sn'][0]
retVal.append({'username': username, 'firstname': firstname, 'lastname': lastname}) retVal.append({'username': username, 'firstname': firstname, 'lastname': lastname})
return retVal return retVal
def modifyUser(self, user, conn, attributes):
try:
if 'username' in attributes:
conn.search('ou=user,{}'.format(self.dn), '(uid={})'.format(attributes['username']))
if conn.entries:
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])]
conn.modify(user.dn, mody)
except Exception as err:
traceback.print_exc()
raise LDAPExcetpion("Something went wrong in LDAP: {}".format(err))
if __name__ == '__main__': if __name__ == '__main__':
a = LDAPController() a = LDAPController()

View File

@ -5,9 +5,10 @@ import geruecht.controller.emailController as ec
from geruecht.model.user import User from geruecht.model.user import User
from geruecht.exceptions import PermissionDenied from geruecht.exceptions import PermissionDenied
from datetime import datetime, timedelta from datetime import datetime, timedelta
from geruecht.exceptions import UsernameExistLDAP, UsernameExistDB, DatabaseExecption, LDAPExcetpion
db = dc.DatabaseController() db = dc.DatabaseController()
ldap = lc.LDAPController(ldapConfig['URL'], ldapConfig['dn']) ldap = lc.LDAPController(ldapConfig['dn'])
emailController = ec.EmailController(mailConfig['URL'], mailConfig['user'], mailConfig['passwd'], mailConfig['port'], mailConfig['email']) emailController = ec.EmailController(mailConfig['URL'], mailConfig['user'], mailConfig['passwd'], mailConfig['port'], mailConfig['email'])
class UserController(metaclass=Singleton): class UserController(metaclass=Singleton):
@ -128,10 +129,31 @@ class UserController(metaclass=Singleton):
retVal.append(self.sendMail(user)) retVal.append(self.sendMail(user))
return retVal return retVal
def modifyUser(self, user, ldap_conn, attributes):
try:
if 'username' in attributes:
db.changeUsername(user, attributes['username'])
ldap.modifyUser(user, ldap_conn, attributes)
if 'username' in attributes:
return self.getUser(attributes['username'])
else:
return self.getUser(user.uid)
except UsernameExistLDAP as err:
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 Exception as err:
raise Exception(err)
def loginUser(self, username, password): def loginUser(self, username, password):
try: try:
user = self.getUser(username) user = self.getUser(username)
user.password = password
ldap.login(username, password) ldap.login(username, password)
return user ldap_conn = ldap.bind(user, password)
return user, ldap_conn
except PermissionDenied as err: except PermissionDenied as err:
raise err raise err

View File

@ -1,2 +1,10 @@
class PermissionDenied(Exception): class PermissionDenied(Exception):
pass pass
class UsernameExistDB(Exception):
pass
class UsernameExistLDAP(Exception):
pass
class DatabaseExecption(Exception):
pass
class LDAPExcetpion(Exception):
pass

View File

@ -4,6 +4,7 @@ from datetime import datetime
import geruecht.controller.userController as uc import geruecht.controller.userController as uc
from geruecht.model import MONEY from geruecht.model import MONEY
from geruecht.decorator import login_required from geruecht.decorator import login_required
import time
finanzer = Blueprint("finanzer", __name__) finanzer = Blueprint("finanzer", __name__)

View File

@ -15,8 +15,9 @@ class AccessToken():
timestamp = None timestamp = None
user = None user = None
token = None token = None
ldap_conn = None
def __init__(self, user, token, timestamp=datetime.now()): def __init__(self, user, token, ldap_conn, timestamp=datetime.now()):
""" Initialize Class AccessToken """ Initialize Class AccessToken
No more to say. No more to say.
@ -30,6 +31,7 @@ class AccessToken():
self.user = user self.user = user
self.timestamp = timestamp self.timestamp = timestamp
self.token = token self.token = token
self.ldap_conn = ldap_conn
def updateTimestamp(self): def updateTimestamp(self):
""" Update the Timestamp """ Update the Timestamp

View File

@ -49,6 +49,7 @@ class User():
self.group = data['gruppe'].split(',') self.group = data['gruppe'].split(',')
if 'creditLists' in data: if 'creditLists' in data:
self.geruechte = data['creditLists'] self.geruechte = data['creditLists']
self.password = ''
def updateData(self, data): def updateData(self, data):
if 'dn' in data: if 'dn' in data:
@ -204,7 +205,8 @@ class User():
"username": self.uid, "username": self.uid,
"locked": self.locked, "locked": self.locked,
"autoLock": self.autoLock, "autoLock": self.autoLock,
"limit": self.limit "limit": self.limit,
"mail": self.mail
} }
return dic return dic

View File

@ -48,9 +48,9 @@ def _login():
password = data['password'] password = data['password']
LOGGER.info("search {} in database".format(username)) LOGGER.info("search {} in database".format(username))
try: try:
user = userController.loginUser(username, password) user, ldap_conn = userController.loginUser(username, password)
user.password = password user.password = password
token = accesTokenController.createAccesToken(user) token = accesTokenController.createAccesToken(user, ldap_conn)
dic = accesTokenController.validateAccessToken(token, [USER]).user.toJSON() dic = accesTokenController.validateAccessToken(token, [USER]).user.toJSON()
dic["token"] = token dic["token"] = token
dic["accessToken"] = token dic["accessToken"] = token
@ -58,5 +58,7 @@ def _login():
return jsonify(dic) return jsonify(dic)
except PermissionDenied as err: except PermissionDenied as err:
return jsonify({"error": str(err)}), 401 return jsonify({"error": str(err)}), 401
except Exception:
return jsonify({"error": "permission denied"}), 401
LOGGER.info("User {} does not exist.".format(username)) LOGGER.info("User {} does not exist.".format(username))
return jsonify({"error": "wrong username"}), 401 return jsonify({"error": "wrong username"}), 401

View File

@ -3,6 +3,8 @@ from geruecht.decorator import login_required
import geruecht.controller.userController as uc import geruecht.controller.userController as uc
from geruecht.model import USER from geruecht.model import USER
from datetime import datetime from datetime import datetime
import time
import traceback
user = Blueprint("user", __name__) user = Blueprint("user", __name__)
@ -34,3 +36,41 @@ def _addAmount(**kwargs):
retVal['creditList'] = {credit.year: credit.toJSON() for credit in accToken.user.geruechte} retVal['creditList'] = {credit.year: credit.toJSON() for credit in accToken.user.geruechte}
return jsonify(retVal) return jsonify(retVal)
return jsonify({"error": "something went wrong"}), 500 return jsonify({"error": "something went wrong"}), 500
@user.route("/user/saveConfig", methods=['POST'])
@login_required(groups=[USER])
def _saveConfig(**kwargs):
try:
if 'accToken' in kwargs:
accToken = kwargs['accToken']
data = request.get_json()
accToken.user = userController.modifyUser(accToken.user, accToken.ldap_conn, data)
retVal = accToken.user.toJSON()
retVal['creditList'] = {credit.year: credit.toJSON() for credit in accToken.user.geruechte}
return jsonify(retVal)
except Exception as err:
return jsonify({"error": err}), 409
@user.route("/user/job", methods=['POST'])
@login_required(groups=[USER])
def _getJob(**kwargs):
try:
if 'accToken' in kwargs:
accToken = kwargs['accToken']
data = request.get_json()
date = datetime.utcfromtimestamp(int(data['date']))
test = userController.getWorker(date, username=accToken.user.uid)
if test == [None]:
job = False
else:
job = True
if job:
workers = userController.getWorker(date)
for worker in workers:
if worker['user']['uid'] == accToken.user.uid:
workers.remove(worker)
return jsonify({'job': job, 'workers': workers})
return jsonify({'job': job})
except Exception as err:
traceback.print_exc()
return jsonify({"error": str(err)}), 409

View File

@ -3,6 +3,7 @@ from datetime import datetime
import geruecht.controller.userController as uc import geruecht.controller.userController as uc
from geruecht.decorator import login_required from geruecht.decorator import login_required
from geruecht.model import MONEY, GASTRO from geruecht.model import MONEY, GASTRO
import time
vorstand = Blueprint("vorstand", __name__) vorstand = Blueprint("vorstand", __name__)
userController = uc.UserController() userController = uc.UserController()