Merge branch 'feature/accessToken' into develop

This commit is contained in:
Tim Gröger 2020-06-05 01:25:05 +02:00
commit a1098713ca
7 changed files with 186 additions and 39 deletions

View File

@ -1,6 +1,7 @@
from geruecht.model.accessToken import AccessToken from geruecht.model.accessToken import AccessToken
import geruecht.controller as gc import geruecht.controller as gc
import geruecht.controller.mainController as mc import geruecht.controller.mainController as mc
import geruecht.controller.databaseController as dc
from geruecht.model import BAR from geruecht.model import BAR
from datetime import datetime, timedelta from datetime import datetime, timedelta
import hashlib import hashlib
@ -10,6 +11,7 @@ from geruecht.logger import getDebugLogger
debug = getDebugLogger() debug = getDebugLogger()
mainController = mc.MainController() mainController = mc.MainController()
db = dc.DatabaseController()
class AccesTokenController(metaclass=Singleton): class AccesTokenController(metaclass=Singleton):
""" Control all createt AccesToken """ Control all createt AccesToken
@ -30,7 +32,6 @@ class AccesTokenController(metaclass=Singleton):
""" """
debug.info("init accesstoken controller") debug.info("init accesstoken controller")
self.lifetime = gc.accConfig self.lifetime = gc.accConfig
self.tokenList = []
def checkBar(self, user): def checkBar(self, user):
debug.info("check if user {{ {} }} is baruser".format(user)) debug.info("check if user {{ {} }} is baruser".format(user))
@ -57,7 +58,7 @@ class AccesTokenController(metaclass=Singleton):
An the AccesToken for this given Token or False. An the AccesToken for this given Token or False.
""" """
debug.info("check token {{ {} }} is valid") debug.info("check token {{ {} }} is valid")
for accToken in self.tokenList: for accToken in db.getAccessTokens():
debug.debug("accesstoken is {}".format(accToken)) debug.debug("accesstoken is {}".format(accToken))
endTime = accToken.timestamp + timedelta(seconds=accToken.lifetime) endTime = accToken.timestamp + timedelta(seconds=accToken.lifetime)
now = datetime.now() now = datetime.now()
@ -69,19 +70,16 @@ class AccesTokenController(metaclass=Singleton):
debug.debug("check if accestoken {{ {} }} has group {{ {} }}".format(accToken, group)) debug.debug("check if accestoken {{ {} }} has group {{ {} }}".format(accToken, group))
if self.isSameGroup(accToken, group): if self.isSameGroup(accToken, group):
accToken.updateTimestamp() accToken.updateTimestamp()
db.updateAccessToken(accToken)
debug.debug("found accesstoken {{ {} }} with token: {{ {} }} and group: {{ {} }}".format(accToken, token, group)) debug.debug("found accesstoken {{ {} }} with token: {{ {} }} and group: {{ {} }}".format(accToken, token, group))
return accToken return accToken
else: else:
debug.debug("accesstoken is {{ {} }} out of date".format(accToken)) debug.debug("accesstoken is {{ {} }} out of date".format(accToken))
self.deleteAccessToken(accToken) db.deleteAccessToken(accToken)
debug.debug("no valid accesstoken with token: {{ {} }} and group: {{ {} }}".format(token, group)) debug.debug("no valid accesstoken with token: {{ {} }} and group: {{ {} }}".format(token, group))
return False return False
def deleteAccessToken(self, accToken): def createAccesToken(self, user, user_agent=None):
debug.info("delete accesstoken {{ {} }}".format(accToken))
self.tokenList.remove(accToken)
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.
@ -96,9 +94,8 @@ 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, ldap_conn, self.lifetime, datetime.now()) accToken = db.createAccessToken(user, token, self.lifetime, datetime.now(), lock_bar=False, user_agent=user_agent)
debug.debug("accesstoken is {{ {} }}".format(accToken)) debug.debug("accesstoken is {{ {} }}".format(accToken))
self.tokenList.append(accToken)
return token return token
def isSameGroup(self, accToken, groups): def isSameGroup(self, accToken, groups):
@ -117,3 +114,13 @@ class AccesTokenController(metaclass=Singleton):
for group in groups: for group in groups:
if group in accToken.user.group: return True if group in accToken.user.group: return True
return False return False
def getAccessTokensFromUser(self, user):
return db.getAccessTokensFromUser(user)
def deleteAccessToken(self, accToken):
db.deleteAccessToken(accToken)
def updateAccessToken(self, accToken):
accToken.updateTimestamp()
return db.updateAccessToken(accToken)

View File

@ -1,6 +1,6 @@
from ..mainController import Singleton from ..mainController import Singleton
from geruecht import db from geruecht import db
from ..databaseController import dbUserController, dbCreditListController, dbJobKindController, dbPricelistController, dbWorkerController, dbWorkgroupController, dbJobInviteController, dbJobRequesController from ..databaseController import dbUserController, dbCreditListController, dbJobKindController, dbPricelistController, dbWorkerController, dbWorkgroupController, dbJobInviteController, dbJobRequesController, dbAccessTokenController
from geruecht.exceptions import DatabaseExecption from geruecht.exceptions import DatabaseExecption
import traceback import traceback
from MySQLdb._exceptions import IntegrityError from MySQLdb._exceptions import IntegrityError
@ -13,6 +13,7 @@ class DatabaseController(dbUserController.Base,
dbJobKindController.Base, dbJobKindController.Base,
dbJobInviteController.Base, dbJobInviteController.Base,
dbJobRequesController.Base, dbJobRequesController.Base,
dbAccessTokenController.Base,
metaclass=Singleton): metaclass=Singleton):
''' '''
DatabaesController DatabaesController

View File

@ -0,0 +1,82 @@
import traceback
from geruecht.exceptions import DatabaseExecption
from geruecht.model.accessToken import AccessToken
class Base:
def getAccessToken(self, item):
try:
cursor = self.db.connection.cursor()
if type(item) == str:
sql = "select * from session where token='{}'".format(item)
elif type(item) == int:
sql = 'select * from session where id={}'.format(item)
else:
raise DatabaseExecption("item as no type int or str. name={}, type={}".format(item, type(item)))
cursor.execute(sql)
session = cursor.fetchone()
retVal = AccessToken(session['id'], self.getUserById(session['user']), session['token'], session['lifetime'], session['timestamp'], browser=session['browser'], platform=session['platform']) if session != None else None
return retVal
except Exception as err:
traceback.print_exc()
self.db.connection.rollback()
raise DatabaseExecption("Something went worng with Databes: {}".format(err))
def getAccessTokensFromUser(self, user):
try:
cursor = self.db.connection.cursor()
cursor.execute("select * from session where user={}".format(user.id))
sessions = cursor.fetchall()
retVal = [
AccessToken(session['id'], self.getUserById(session['user']), session['token'], session['lifetime'],
session['timestamp'], browser=session['browser'], platform=session['platform']) for session in sessions]
return retVal
except Exception as err:
traceback.print_exc()
self.db.connection.rollback()
raise DatabaseExecption("Something went worng with Datatabase: {}".format(err))
def getAccessTokens(self):
try:
cursor = self.db.connection.cursor()
cursor.execute("select * from session")
sessions = cursor.fetchall()
retVal = [AccessToken(session['id'], self.getUserById(session['user']), session['token'], session['lifetime'], session['timestamp'], browser=session['browser'], platform=session['platform']) for session in sessions]
return retVal
except Exception as err:
traceback.print_exc()
self.db.connection.rollback()
raise DatabaseExecption("Something went worng with Datatabase: {}".format(err))
def createAccessToken(self, user, token, lifetime, timestamp, lock_bar, user_agent=None):
try:
cursor = self.db.connection.cursor()
cursor.execute("insert into session (user, timestamp, lock_bar, token, lifetime, browser, platform) VALUES ({}, '{}', {}, '{}', {}, '{}', '{}')".format(user.id, timestamp, lock_bar, token, lifetime, user_agent.browser if user_agent else 'NULL', user_agent.platform if user_agent else 'NULL'))
self.db.connection.commit()
return self.getAccessToken(token)
except Exception as err:
traceback.print_exc()
self.db.connection.rollback()
raise DatabaseExecption("Something went worng with Datatabase: {}".format(err))
def updateAccessToken(self, accToken):
try:
cursor = self.db.connection.cursor()
cursor.execute("update session set timestamp='{}', lock_bar={}, lifetime={} where id={}".format(accToken.timestamp, accToken.lock_bar, accToken.lifetime, accToken.id))
self.db.connection.commit()
return self.getAccessToken(accToken.id)
except Exception as err:
traceback.print_exc()
self.db.connection.rollback()
raise DatabaseExecption("Something went worng with Datatabase: {}".format(err))
def deleteAccessToken(self, accToken):
try:
cursor = self.db.connection.cursor()
cursor.execute("delete from session where id={}".format(accToken.id))
self.db.connection.commit()
except Exception as err:
traceback.print_exc()
self.db.connection.rollback()
raise DatabaseExecption("Something went worng with Datatabase: {}".format(err))

View File

@ -1,3 +1,5 @@
from ldap3.core.exceptions import LDAPPasswordIsMandatoryError, LDAPBindError
from geruecht.exceptions import UsernameExistLDAP, LDAPExcetpion, PermissionDenied from geruecht.exceptions import UsernameExistLDAP, LDAPExcetpion, PermissionDenied
import geruecht.controller.databaseController as dc import geruecht.controller.databaseController as dc
import geruecht.controller.ldapController as lc import geruecht.controller.ldapController as lc
@ -114,22 +116,27 @@ class Base:
debug.debug("user is {{ {} }}".format(user)) debug.debug("user is {{ {} }}".format(user))
return user return user
def modifyUser(self, user, ldap_conn, attributes): def modifyUser(self, user, attributes, password):
debug.info("modify user {{ {} }} with attributes {{ {} }} with ldap_conn {{ {} }}".format( debug.info("modify user {{ {} }} with attributes {{ {} }}".format(
user, attributes, ldap_conn)) user, attributes))
try: try:
if 'username' in attributes: ldap_conn = ldap.bind(user, password)
debug.debug("change username, so change first in database") if attributes:
db.changeUsername(user, attributes['username']) if 'username' in attributes:
ldap.modifyUser(user, ldap_conn, attributes) debug.debug("change username, so change first in database")
if 'username' in attributes: db.changeUsername(user, attributes['username'])
retVal = self.getUser(attributes['username']) ldap.modifyUser(user, ldap_conn, attributes)
debug.debug("user is {{ {} }}".format(retVal)) if 'username' in attributes:
return retVal retVal = self.getUser(attributes['username'])
else: debug.debug("user is {{ {} }}".format(retVal))
retVal = self.getUser(user.uid) return retVal
debug.debug("user is {{ {} }}".format(retVal)) else:
return retVal retVal = self.getUser(user.uid)
debug.debug("user is {{ {} }}".format(retVal))
return retVal
return self.getUser(user.uid)
except UsernameExistLDAP as err: except UsernameExistLDAP as err:
debug.debug( debug.debug(
"username exists on ldap, rechange username on database", exc_info=True) "username exists on ldap, rechange username on database", exc_info=True)
@ -139,6 +146,10 @@ class Base:
if 'username' in attributes: if 'username' in attributes:
db.changeUsername(user, user.uid) db.changeUsername(user, user.uid)
raise Exception(err) 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: except Exception as err:
raise Exception(err) raise Exception(err)
@ -153,8 +164,7 @@ class Base:
debug.debug("user is {{ {} }}".format(user)) debug.debug("user is {{ {} }}".format(user))
user.password = password user.password = password
ldap.login(username, password) ldap.login(username, password)
ldap_conn = ldap.bind(user, password) return user
return user, ldap_conn
except PermissionDenied as err: except PermissionDenied as err:
debug.debug("permission is denied", exc_info=True) debug.debug("permission is denied", exc_info=True)
raise err raise err

View File

@ -15,9 +15,8 @@ class AccessToken():
timestamp = None timestamp = None
user = None user = None
token = None token = None
ldap_conn = None
def __init__(self, user, token, ldap_conn, lifetime, timestamp=datetime.now()): def __init__(self, id, user, token, lifetime, timestamp=datetime.now(), browser=None, platform=None):
""" Initialize Class AccessToken """ Initialize Class AccessToken
No more to say. No more to say.
@ -28,12 +27,14 @@ class AccessToken():
timestamp: Default current time, but can set to an other datetime-Object. timestamp: Default current time, but can set to an other datetime-Object.
""" """
debug.debug("init accesstoken") debug.debug("init accesstoken")
self.id = id
self.user = user self.user = user
self.timestamp = timestamp self.timestamp = timestamp
self.lifetime = lifetime self.lifetime = lifetime
self.token = token self.token = token
self.ldap_conn = ldap_conn
self.lock_bar = False self.lock_bar = False
self.browser = browser
self.platform = platform
debug.debug("accesstoken is {{ {} }}".format(self)) debug.debug("accesstoken is {{ {} }}".format(self))
def updateTimestamp(self): def updateTimestamp(self):
@ -44,6 +45,27 @@ class AccessToken():
debug.debug("update timestamp from accesstoken {{ {} }}".format(self)) debug.debug("update timestamp from accesstoken {{ {} }}".format(self))
self.timestamp = datetime.now() self.timestamp = datetime.now()
def toJSON(self):
""" Create Dic to dump in JSON
Returns:
A Dic with static Attributes.
"""
dic = {
"id": self.id,
"timestamp": {'year': self.timestamp.year,
'month': self.timestamp.month,
'day': self.timestamp.day,
'hour': self.timestamp.hour,
'minute': self.timestamp.minute,
'second': self.timestamp.second
},
"lifetime": self.lifetime,
"browser": self.browser,
"platform": self.platform
}
return dic
def __eq__(self, token): def __eq__(self, token):
return True if self.token == token else False return True if self.token == token else False

View File

@ -132,7 +132,8 @@ def _saveLifeTime(**kwargs):
lifetime, accToken)) lifetime, accToken))
accToken.lifetime = lifetime accToken.lifetime = lifetime
debug.info("update accesstoken timestamp") debug.info("update accesstoken timestamp")
accToken.updateTimestamp() accToken = accesTokenController.updateAccessToken(accToken)
accToken = accesTokenController.validateAccessToken(accToken.token, [USER, EXTERN])
retVal = {"value": accToken.lifetime, retVal = {"value": accToken.lifetime,
"group": accToken.user.toJSON()['group']} "group": accToken.user.toJSON()['group']}
debug.info( debug.info(
@ -143,7 +144,6 @@ def _saveLifeTime(**kwargs):
"exception in save lifetime for accesstoken.", exc_info=True) "exception in save lifetime for accesstoken.", exc_info=True)
return jsonify({"error": str(err)}), 500 return jsonify({"error": str(err)}), 500
@app.route("/logout", methods=['GET']) @app.route("/logout", methods=['GET'])
@login_required(groups=[MONEY, GASTRO, VORSTAND, EXTERN, USER], bar=True) @login_required(groups=[MONEY, GASTRO, VORSTAND, EXTERN, USER], bar=True)
def _logout(**kwargs): def _logout(**kwargs):
@ -177,11 +177,11 @@ def _login():
password = data['password'] password = data['password']
debug.debug("username is {{ {} }}".format(username)) debug.debug("username is {{ {} }}".format(username))
try: try:
user_agent = request.user_agent
debug.info("search {{ {} }} in database".format(username)) debug.info("search {{ {} }} in database".format(username))
user, ldap_conn = mainController.loginUser(username, password) user = mainController.loginUser(username, password)
debug.debug("user is {{ {} }}".format(user)) debug.debug("user is {{ {} }}".format(user))
user.password = password token = accesTokenController.createAccesToken(user, user_agent=user_agent)
token = accesTokenController.createAccesToken(user, ldap_conn)
debug.debug("accesstoken is {{ {} }}".format(token)) debug.debug("accesstoken is {{ {} }}".format(token))
debug.info("validate accesstoken") debug.info("validate accesstoken")
dic = accesTokenController.validateAccessToken( dic = accesTokenController.validateAccessToken(
@ -194,6 +194,6 @@ def _login():
except PermissionDenied as err: except PermissionDenied as err:
debug.warning("permission denied exception in logout", exc_info=True) debug.warning("permission denied exception in logout", exc_info=True)
return jsonify({"error": str(err)}), 401 return jsonify({"error": str(err)}), 401
except Exception: except Exception as err:
debug.warning("exception in logout.", exc_info=True) debug.warning("exception in logout.", exc_info=True)
return jsonify({"error": "permission denied"}), 401 return jsonify({"error": "permission denied"}), 401

View File

@ -1,14 +1,17 @@
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from geruecht.decorator import login_required from geruecht.decorator import login_required
import geruecht.controller.mainController as mc import geruecht.controller.mainController as mc
import geruecht.controller.accesTokenController as ac
from geruecht.model import USER from geruecht.model import USER
from datetime import datetime, time, date from datetime import datetime, time, date
from geruecht.exceptions import DayLocked from geruecht.exceptions import DayLocked
from geruecht.logger import getDebugLogger, getCreditLogger, getJobsLogger from geruecht.logger import getDebugLogger, getCreditLogger, getJobsLogger
from geruecht.model.accessToken import AccessToken
user = Blueprint("user", __name__) user = Blueprint("user", __name__)
mainController = mc.MainController() mainController = mc.MainController()
accesTokenController = ac.AccesTokenController()
debug = getDebugLogger() debug = getDebugLogger()
creditL = getCreditLogger() creditL = getCreditLogger()
@ -66,8 +69,10 @@ def _saveConfig(**kwargs):
if 'accToken' in kwargs: if 'accToken' in kwargs:
accToken = kwargs['accToken'] accToken = kwargs['accToken']
data = request.get_json() data = request.get_json()
password = data['acceptedPassword']
data.pop('acceptedPassword')
accToken.user = mainController.modifyUser( accToken.user = mainController.modifyUser(
accToken.user, accToken.ldap_conn, data) accToken.user, data, password)
retVal = accToken.user.toJSON() retVal = accToken.user.toJSON()
retVal['creditList'] = {credit.year: credit.toJSON() retVal['creditList'] = {credit.year: credit.toJSON()
for credit in accToken.user.geruechte} for credit in accToken.user.geruechte}
@ -385,4 +390,24 @@ def _deleteJobRequest(**kwargs):
return jsonify(retVal) return jsonify(retVal)
except Exception as err: except Exception as err:
debug.debug("exception", exc_info=True) debug.debug("exception", exc_info=True)
return jsonify({"error": str(err)}), 500 return jsonify({"error": str(err)}), 500
@user.route("/user/getAccessTokens", methods=['GET', 'POST'])
@login_required(groups=[USER])
def _getAccessTokens(**kwargs):
try:
debug.info("/user/getAccessTokens")
if request.method == 'POST':
data = request.get_json()
delAccToken = AccessToken(data['id'], kwargs['accToken'].user, None, None, None)
accesTokenController.deleteAccessToken(delAccToken)
tokens = accesTokenController.getAccessTokensFromUser(kwargs['accToken'].user)
retVal = []
for token in tokens:
retVal.append(token.toJSON())
debug.debug("return {{ {} }}".format(retVal))
return jsonify(retVal)
except Exception as err:
debug.debug("exception", exc_info=True)
return jsonify({"error": str(err)}), 500