Added modules for authentification.

* Added base class for auth plugins
  * Provide plain_auth (using password authentification)
  * Provide module for login and logout handling
This commit is contained in:
Ferdinand Thiessen 2020-08-22 16:47:56 +02:00
parent 187dc40730
commit a000ccfb1c
16 changed files with 256 additions and 664 deletions

View File

@ -19,6 +19,11 @@ with (_modpath/'logging.yml').open(mode='rb') as file:
config = yaml.safe_load(file.read()) config = yaml.safe_load(file.read())
dictConfig(config) dictConfig(config)
import logging
from werkzeug.local import LocalProxy
logger = LocalProxy(lambda: logging.getLogger(__name__))
def create_app(): def create_app():
app = Flask(__name__) app = Flask(__name__)
CORS(app) CORS(app)

View File

@ -37,11 +37,6 @@ handlers:
encoding: utf8 encoding: utf8
loggers: loggers:
debug_logger:
level: DEBUG
handlers: [console, debug]
propagate: no
credit_logger: credit_logger:
level: INFO level: INFO
handlers: [credit] handlers: [credit]
@ -52,6 +47,9 @@ loggers:
handlers: [jobs] handlers: [jobs]
propagate: no propagate: no
werkzeug:
level: WARNING
root: root:
level: INFO level: DEBUG
handlers: [console, debug] handlers: [console, debug]

View File

@ -0,0 +1,29 @@
class Auth():
def login(self, user, pw):
"""
user User class containing at least the uid
pw given password
HAS TO BE IMPLEMENTED!
should return False if not found or invalid credentials
should return True if success
"""
return False
def updateUser(self, user):
"""
user User class
If backend is using external data, then update this user instance with external data
"""
pass
def modifyUser(self, user):
"""
user User class
If backend is using (writeable) external data, then update the external database with the user provided.
"""
pass

View File

@ -0,0 +1,77 @@
#############################################
# Plugin: Auth #
# Functionality: Allow management of #
# authentification, login, logout, etc #
#############################################
from flask import Blueprint, current_app, request, jsonify
from werkzeug.local import LocalProxy
from flaschengeist.system.decorator import login_required
from flaschengeist.system.exceptions import PermissionDenied
from flaschengeist.system.controller import mainController as mc
import flaschengeist.system.controller.accesTokenController as ac
logger = LocalProxy(lambda: current_app.logger)
accesTokenController = LocalProxy(lambda: ac.AccesTokenController())
auth_bp = Blueprint('auth', __name__)
def register():
return auth_bp
############################################
## Routes ##
############################################
@auth_bp.route("/logout", methods=['GET'])
@login_required()
def _logout(**kwargs):
try:
logger.debug("logout user")
accToken = kwargs['accToken']
logger.debug("accesstoken is {{ {} }}".format(accToken))
logger.debug("delete accesstoken")
accesTokenController.deleteAccessToken(accToken)
logger.info("return ok logout user")
return jsonify({"ok": "ok"})
except Exception as err:
logger.warning("exception in logout user.", exc_info=True)
return jsonify({"error": str(err)}), 500
@auth_bp.route("/login", methods=['POST'])
def _login():
""" Login User
Nothing to say.
Login in User and create an AccessToken for the User.
Returns:
A JSON-File with createt Token or Errors
"""
logger.debug("Start log in.")
data = request.get_json()
logger.info(request)
username = data['username']
password = data['password']
logger.debug("username is {{ {} }}".format(username))
try:
logger.debug("search {{ {} }} in database".format(username))
mainController = mc.MainController()
user = mainController.loginUser(username, password)
logger.debug("user is {{ {} }}".format(user))
token = accesTokenController.createAccesToken(user, user_agent=request.user_agent)
logger.debug("accesstoken is {{ {} }}".format(token))
logger.debug("validate accesstoken")
dic = user.toJSON()
dic["token"] = token
logger.info("User {{ {} }} success login.".format(username))
logger.debug("return login {{ {} }}".format(dic))
return jsonify(dic)
except PermissionDenied as err:
logger.debug("permission denied exception in login", exc_info=True)
return jsonify({"error": str(err)}), 401
except Exception as err:
logger.error("exception in login.", exc_info=True)
return jsonify({"error": "permission denied"}), 401

View File

@ -0,0 +1,24 @@
import hashlib, binascii, os
import flaschengeist.modules as modules
class AuthPlain(modules.Auth):
def login(self, user, password):
if not user:
return False
if 'password' in user.attributes:
return self.__verify_password(user.attributes['password'].value, password)
return False
def __hash_password(self, password):
salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
pwdhash = hashlib.pbkdf2_hmac('sha3-512', password.encode('utf-8'), salt, 100000)
pwdhash = binascii.hexlify(pwdhash)
return (salt + pwdhash).decode('ascii')
def __verify_password(self, stored_password, provided_password):
salt = stored_password[:64]
stored_password = stored_password[64:]
pwdhash = hashlib.pbkdf2_hmac('sha3-512', provided_password.encode('utf-8'),
salt.encode('ascii'), 100000)
pwdhash = binascii.hexlify(pwdhash).decode('ascii')
return pwdhash == stored_password

View File

@ -1,56 +0,0 @@
#######################################
# Plugin: Users #
# Functionality: Allow management #
# of users, login, logout, etc #
#######################################
from flask import Blueprint
from flaschengeist.app import app
def register():
return Blueprint('user', __name__)
#######################################
## Routes ##
#######################################
#dummy
@app.route("/")
def _dummy():
return 'Noch funktioniert hier mal überhaupt nichts!'
@app.route("/login", methods=['POST'])
def _login():
""" Login User
Nothing to say.
Login in User and create an AccessToken for the User.
Returns:
A JSON-File with createt Token or Errors
"""
debug.info("Start log in.")
data = request.get_json()
username = data['username']
password = data['password']
debug.debug("username is {{ {} }}".format(username))
try:
user_agent = request.user_agent
debug.info("search {{ {} }} in database".format(username))
user = mainController.loginUser(username, password)
debug.debug("user is {{ {} }}".format(user))
token = accesTokenController.createAccesToken(user, user_agent=user_agent)
debug.debug("accesstoken is {{ {} }}".format(token))
debug.info("validate accesstoken")
dic = accesTokenController.validateAccessToken(
token, [USER, EXTERN]).user.toJSON()
dic["token"] = token
dic["accessToken"] = token
debug.info("User {{ {} }} success login.".format(username))
debug.info("return login {{ {} }}".format(dic))
return jsonify(dic)
except PermissionDenied as err:
debug.warning("permission denied exception in logout", exc_info=True)
return jsonify({"error": str(err)}), 401
except Exception as err:
debug.warning("exception in logout.", exc_info=True)
return jsonify({"error": "permission denied"}), 401

View File

@ -1,216 +0,0 @@
from geruecht import app
from geruecht.logger import getDebugLogger
from geruecht.decorator import login_required
from geruecht.exceptions import PermissionDenied
import geruecht.controller.accesTokenController as ac
import geruecht.controller.mainController as mc
from geruecht.model import MONEY, BAR, USER, GASTRO, VORSTAND, EXTERN
from flask import request, jsonify
accesTokenController = ac.AccesTokenController()
mainController = mc.MainController()
debug = getDebugLogger()
@app.route("/valid", methods=['POST'])
@login_required(bar=True)
def _valid(**kwargs):
debug.info('/valid')
try:
accToken = kwargs['accToken']
data = request.get_json()
mainController.validateUser(accToken.user.uid, data['password'])
debug.debug('return {{ "ok": "ok" }}')
return jsonify({"ok": "ok"})
except Exception as err:
debug.warning("exception in valide.", exc_info=True)
return jsonify({"error": str(err)}), 500
@app.route("/pricelist", methods=['GET'])
def _getPricelist():
try:
debug.info("get pricelist")
retVal = mainController.getPricelist()
debug.info("return pricelist {{ {} }}".format(retVal))
return jsonify(retVal)
except Exception as err:
debug.warning("exception in get pricelist.", exc_info=True)
return jsonify({"error": str(err)}), 500
@app.route('/drinkTypes', methods=['GET'])
def getTypes():
try:
debug.info("get drinktypes")
retVal = mainController.getAllDrinkTypes()
debug.info("return drinktypes {{ {} }}".format(retVal))
return jsonify(retVal)
except Exception as err:
debug.warning("exception in get drinktypes.", exc_info=True)
return jsonify({"error": str(err)}), 500
@app.route('/getAllStatus', methods=['GET'])
@login_required(groups=[USER, MONEY, GASTRO, BAR, VORSTAND], bar=True)
def _getAllStatus(**kwargs):
try:
debug.info("get all status for users")
retVal = mainController.getAllStatus()
debug.info("return all status for users {{ {} }}".format(retVal))
return jsonify(retVal)
except Exception as err:
debug.warning("exception in get all status for users.", exc_info=True)
return jsonify({"error": str(err)}), 500
@app.route('/getStatus', methods=['POST'])
@login_required(groups=[USER, MONEY, GASTRO, BAR, VORSTAND], bar=True)
def _getStatus(**kwargs):
try:
debug.info("get status from user")
data = request.get_json()
name = data['name']
debug.info("get status from user {{ {} }}".format(name))
retVal = mainController.getStatus(name)
debug.info(
"return status from user {{ {} }} : {{ {} }}".format(name, retVal))
return jsonify(retVal)
except Exception as err:
debug.warning("exception in get status from user.", exc_info=True)
return jsonify({"error": str(err)}), 500
@app.route('/getUsers', methods=['GET'])
@login_required(groups=[USER], bar=True)
def _getUsers(**kwargs):
try:
extern = True
if 'extern' in request.args:
extern = not bool(int(request.args['extern']))
debug.info("get all users from database")
users = mainController.getAllUsersfromDB(extern=extern)
debug.debug("users are {{ {} }}".format(users))
retVal = [user.toJSON() for user in users]
debug.info("return all users from database {{ {} }}".format(retVal))
return jsonify(retVal)
except Exception as err:
debug.warning(
"exception in get all users from database.", exc_info=True)
return jsonify({"error": str(err)}), 500
@app.route("/getLifeTime", methods=['GET'])
@login_required(groups=[MONEY, GASTRO, VORSTAND, EXTERN, USER], bar=True)
def _getLifeTime(**kwargs):
try:
debug.info("get lifetime of accesstoken")
if 'accToken' in kwargs:
accToken = kwargs['accToken']
debug.debug("accessToken is {{ {} }}".format(accToken))
retVal = {"value": accToken.lifetime,
"group": accToken.user.toJSON()['group'],
"lock_bar": accToken.lock_bar}
debug.info(
"return get lifetime from accesstoken {{ {} }}".format(retVal))
return jsonify(retVal)
except Exception as err:
debug.info("exception in get lifetime of accesstoken.", exc_info=True)
return jsonify({"error": str(err)}), 500
@app.route("/saveLifeTime", methods=['POST'])
@login_required(groups=[MONEY, GASTRO, VORSTAND, EXTERN, USER], bar=True)
def _saveLifeTime(**kwargs):
try:
debug.info("save lifetime for accessToken")
if 'accToken' in kwargs:
accToken = kwargs['accToken']
debug.debug("accessToken is {{ {} }}".format(accToken))
data = request.get_json()
lifetime = data['value']
debug.debug("lifetime is {{ {} }}".format(lifetime))
debug.info("set lifetime {{ {} }} to accesstoken {{ {} }}".format(
lifetime, accToken))
accToken.lifetime = lifetime
debug.info("update accesstoken timestamp")
accToken = accesTokenController.updateAccessToken(accToken)
accToken = accesTokenController.validateAccessToken(accToken.token, [USER, EXTERN])
retVal = {"value": accToken.lifetime,
"group": accToken.user.toJSON()['group']}
debug.info(
"return save lifetime for accessToken {{ {} }}".format(retVal))
return jsonify(retVal)
except Exception as err:
debug.warning(
"exception in save lifetime for accesstoken.", exc_info=True)
return jsonify({"error": str(err)}), 500
@app.route("/passwordReset", methods=['POST'])
def _passwordReset():
try:
debug.info('password reset')
data = request.get_json()
mail = mainController.resetPassword(data)
index = mail.find('@')
for i in range(index):
if i == 0:
continue
mail = mail.replace(mail[i], "*", 1)
return jsonify({"ok": "ok", "mail": mail})
except Exception as err:
debug.warning("excetpion in password reset", exc_info=True)
return jsonify({"error": str(err)}), 409
@app.route("/logout", methods=['GET'])
@login_required(groups=[MONEY, GASTRO, VORSTAND, EXTERN, USER], bar=True)
def _logout(**kwargs):
try:
debug.info("logout user")
if 'accToken' in kwargs:
accToken = kwargs['accToken']
debug.debug("accesstoken is {{ {} }}".format(accToken))
debug.info("delete accesstoken")
accesTokenController.deleteAccessToken(accToken)
debug.info("return ok logout user")
return jsonify({"ok": "ok"})
except Exception as err:
debug.warning("exception in logout user.", exc_info=True)
return jsonify({"error": str(err)}), 500
@app.route("/login", methods=['POST'])
def _login():
""" Login User
Nothing to say.
Login in User and create an AccessToken for the User.
Returns:
A JSON-File with createt Token or Errors
"""
debug.info("Start log in.")
data = request.get_json()
username = data['username']
password = data['password']
debug.debug("username is {{ {} }}".format(username))
try:
user_agent = request.user_agent
debug.info("search {{ {} }} in database".format(username))
user = mainController.loginUser(username, password)
debug.debug("user is {{ {} }}".format(user))
token = accesTokenController.createAccesToken(user, user_agent=user_agent)
debug.debug("accesstoken is {{ {} }}".format(token))
debug.info("validate accesstoken")
dic = accesTokenController.validateAccessToken(
token, [USER, EXTERN]).user.toJSON()
dic["token"] = token
dic["accessToken"] = token
debug.info("User {{ {} }} success login.".format(username))
debug.info("return login {{ {} }}".format(dic))
return jsonify(dic)
except PermissionDenied as err:
debug.warning("permission denied exception in logout", exc_info=True)
return jsonify({"error": str(err)}), 401
except Exception as err:
debug.warning("exception in logout.", exc_info=True)
return jsonify({"error": "permission denied"}), 401

View File

@ -1,244 +0,0 @@
from geruecht.logger import getDebugLogger
from geruecht.model.creditList import CreditList, create_empty_data
from datetime import datetime
debug = getDebugLogger()
class User():
""" Database Object for User
Table for all safed User
Attributes:
id: Id in Database as Primary Key.
userID: ID for the User maybe to Link?
username: Username of the User to Login
firstname: Firstname of the User
Lastname: Lastname of the User
group: Which group is the User? moneymaster, gastro, user or bar?
password: salted hashed password for the User.
"""
def __init__(self, data):
debug.info("init user")
if 'id' in data:
self.id = int(data['id'])
self.uid = data['uid']
self.dn = data['dn']
self.firstname = data['firstname']
self.lastname = data['lastname']
self.group = data['gruppe']
self.last_seen = None
if 'last_seen' in data:
self.last_seen = data['last_seen']
if 'statusgroup' in data:
self.statusgroup = data['statusgroup']
else:
self.statusgroup = None
if 'voting' in data:
self.voting = data['voting']
else:
self.voting = None
if 'mail' in data:
self.mail = data['mail']
else:
self.mail = ''
if 'lockLimit' in data:
self.limit = int(data['lockLimit'])
else:
self.limit = 4200
if 'locked' in data:
self.locked = bool(data['locked'])
else:
self.locked = False
if 'autoLock' in data:
self.autoLock = bool(data['autoLock'])
else:
self.autoLock = True
if type(data['gruppe']) == list:
self.group = data['gruppe']
elif type(data['gruppe']) == str:
self.group = data['gruppe'].split(',')
if 'creditLists' in data:
self.geruechte = data['creditLists']
if 'workgroups' in data:
self.workgroups = data['workgroups']
else:
self.workgroups = None
self.password = ''
debug.debug("user is {{ {} }}".format(self))
def updateData(self, data):
debug.info("update data of user")
if 'dn' in data:
self.dn = data['dn']
if 'firstname' in data:
self.firstname = data['firstname']
if 'lastname' in data:
self.lastname = data['lastname']
if 'gruppe' in data:
self.group = data['gruppe']
if 'lockLimit' in data:
self.limit = int(data['lockLimit'])
if 'locked' in data:
self.locked = bool(data['locked'])
if 'autoLock' in data:
self.autoLock = bool(data['autoLock'])
if 'mail' in data:
self.mail = data['mail']
if 'statusgorup' in data:
self.statusgroup = data['statusgroup']
if 'voting' in data:
self.voting = data['voting']
if 'workgroups' in data:
self.workgroups = data['workgroups']
else:
self.workgroups = None
def initGeruechte(self, creditLists):
if type(creditLists) == list:
self.geruechte = creditLists
def createGeruecht(self, amount=0, year=datetime.now().year):
""" Create Geruecht
This function create a geruecht for the user for an year.
By default is amount zero and year the actual year.
Args:
amount: is the last_schulden of the geruecht
year: is the year of the geruecht
Returns:
the created geruecht
"""
debug.info("create creditlist for user {{ {} }} in year {{ {} }}".format(self, year))
data = create_empty_data()
data['user_id'] = self.id
data['last_schulden'] = amount
data['year_date'] = year
credit = CreditList(data)
self.geruechte.append(credit)
debug.debug("creditlist is {{ {} }}".format(credit))
return credit
def getGeruecht(self, year=datetime.now().year):
""" Get Geruecht
This function returns the geruecht of an year.
By default is the year the actual year.
Args:
year: the year of the geruecht
Returns:
the geruecht of the year
"""
debug.info("get creditlist from user on year {{ {} }}".format(year))
for geruecht in self.geruechte:
if geruecht.year == year:
debug.debug("creditlist is {{ {} }} for user {{ {} }}".format(geruecht, self))
return geruecht
debug.debug("no creditlist found for user {{ {} }}".format(self))
geruecht = self.createGeruecht(year=year)
return self.getGeruecht(year=year)
def addAmount(self, amount, year=datetime.now().year, month=datetime.now().month):
""" Add Amount
This function add an amount to a geruecht with an spezified year and month to the user.
By default the year is the actual year.
By default the month is the actual month.
Args:
year: year of the geruecht
month: month for the amount
Returns:
double (credit, amount)
"""
debug.info("add amount to user {{ {} }} in year {{ {} }} and month {{ {} }}".format(self, year, month))
geruecht = self.getGeruecht(year=year)
retVal = geruecht.addAmount(amount, month=month)
return retVal
def addCredit(self, credit, year=datetime.now().year, month=datetime.now().month):
""" Add Credit
This function add an credit to a geruecht with an spezified year and month to the user.
By default the year is the actual year.
By default the month is the actual month.
Args:
year: year of the geruecht
month: month for the amount
Returns:
double (credit, amount)
"""
debug.info("add credit to user {{ {} }} in year {{ {} }} and month {{ {} }}".format(self, year, month))
geruecht = self.getGeruecht(year=year)
retVal = geruecht.addCredit(credit, month=month)
return retVal
def updateGeruecht(self):
""" Update list of geruechte
This function iterate through the geruechte, which sorted by year and update the last_schulden of the geruecht.
"""
debug.info("update all creditlists ")
self.geruechte.sort(key=self.sortYear)
for index, geruecht in enumerate(self.geruechte):
if index == 0 or index == len(self.geruechte) - 1:
geruecht.last_schulden = 0
if index != 0:
geruecht.last_schulden = (self.geruechte[index - 1].getSchulden() * -1)
return self.geruechte
def sortYear(self, geruecht):
""" Sort Year
This function is only an helperfunction to sort the list of geruechte by years.
It only returns the year of the geruecht.
Args:
geruecht: geruecht which year you want
Returns:
int year of the geruecht
"""
return geruecht.year
def toJSON(self):
""" Create Dic to dump in JSON
Returns:
A Dic with static Attributes.
"""
dic = {
"id": self.id,
"userId": self.uid,
"uid": self.uid,
"dn": self.dn,
"firstname": self.firstname,
"lastname": self.lastname,
"group": self.group,
"username": self.uid,
"locked": self.locked,
"autoLock": self.autoLock,
"limit": self.limit,
"mail": self.mail,
"statusgroup": self.statusgroup,
"voting": self.voting,
"workgroups": self.workgroups
}
return dic
def __repr__(self):
return "User({}, {}, {})".format(self.uid, self.dn, self.group)

View File

@ -1,12 +1,8 @@
from ..logger import getDebugLogger
from ..configparser import ConifgParser from ..configparser import ConifgParser
from flaschengeist import _modpath from flaschengeist import _modpath
import os
config = ConifgParser(_modpath/'config.yml') config = ConifgParser(_modpath/'config.yml')
LOGGER = getDebugLogger()
class Singleton(type): class Singleton(type):
_instances = {} _instances = {}
def __call__(cls, *args, **kwargs): def __call__(cls, *args, **kwargs):

View File

@ -1,17 +1,13 @@
from geruecht.model.accessToken import AccessToken from ..models.accessToken import AccessToken
import geruecht.controller as gc from flaschengeist.system.database import db
import geruecht.controller.mainController as mc
import geruecht.controller.databaseController as dc
from geruecht.model import BAR
from datetime import datetime, timedelta from datetime import datetime, timedelta
import hashlib import secrets
from . import Singleton from . import Singleton
from geruecht.logger import getDebugLogger from flask import Blueprint, request, jsonify
import logging
debug = getDebugLogger() logger = logging.getLogger("flaschenpost")
mainController = mc.MainController()
db = dc.DatabaseController()
class AccesTokenController(metaclass=Singleton): class AccesTokenController(metaclass=Singleton):
""" Control all createt AccesToken """ Control all createt AccesToken
@ -30,22 +26,8 @@ class AccesTokenController(metaclass=Singleton):
Initialize Thread and set tokenList empty. Initialize Thread and set tokenList empty.
""" """
debug.info("init accesstoken controller") logger.debug("init accesstoken controller")
self.lifetime = gc.accConfig self.lifetime = lifetime
def checkBar(self, user):
debug.info("check if user {{ {} }} is baruser".format(user))
if (mainController.checkBarUser(user)):
if BAR not in user.group:
debug.debug("append bar to user {{ {} }}".format(user))
user.group.append(BAR)
return True
else:
while BAR in user.group:
debug.debug("delete bar from user {{ {} }}".format(user))
user.group.remove(BAR)
return False
debug.debug("user {{ {} }} groups are {{ {} }}".format(user, user.group))
def validateAccessToken(self, token, group): def validateAccessToken(self, token, group):
""" Verify Accestoken """ Verify Accestoken
@ -59,27 +41,27 @@ class AccesTokenController(metaclass=Singleton):
Returns: Returns:
An the AccesToken for this given Token or False. An the AccesToken for this given Token or False.
""" """
debug.info("check token {{ {} }} is valid") logger.debug("check token {{ {} }} is valid".format(token))
for accToken in db.getAccessTokens(): for accToken in AccessToken.query.filter_by(token=token):
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.utcnow()
debug.debug("now is {{ {} }}, endtime is {{ {} }}".format(now, endTime)) logger.debug("now is {{ {} }}, endtime is {{ {} }}".format(now, endTime))
if now <= endTime: if now <= endTime:
debug.debug("check if token {{ {} }} is same as {{ {} }}".format(token, accToken)) logger.debug("check if token {{ {} }} is same as {{ {} }}".format(token, accToken))
if accToken == token: if accToken == token:
if not self.checkBar(accToken.user): # if not self.checkBar(accToken.user):
accToken.lock_bar = False # accToken.lock_bar = False
debug.debug("check if accestoken {{ {} }} has group {{ {} }}".format(accToken, group)) # logger.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) db.session.commit()
debug.debug("found accesstoken {{ {} }} with token: {{ {} }} and group: {{ {} }}".format(accToken, token, group)) # logger.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)) logger.debug("accesstoken is {{ {} }} out of date".format(accToken))
db.deleteAccessToken(accToken) db.session.delete(accToken)
debug.debug("no valid accesstoken with token: {{ {} }} and group: {{ {} }}".format(token, group)) db.session.commit()
logger.debug("no valid accesstoken with token: {{ {} }} and group: {{ {} }}".format(token, group))
return False return False
def createAccesToken(self, user, user_agent=None): def createAccesToken(self, user, user_agent=None):
@ -93,12 +75,13 @@ class AccesTokenController(metaclass=Singleton):
Returns: Returns:
A created Token for User A created Token for User
""" """
debug.info("creat accesstoken") logger.debug("creat accesstoken")
now = datetime.ctime(datetime.now()) token = secrets.token_hex(16)
token = hashlib.md5((now + user.dn).encode('utf-8')).hexdigest() accToken = AccessToken(token=token, user=user, lifetime=self.lifetime, browser=user_agent.browser, platform=user_agent.platform)
self.checkBar(user) db.session.add(accToken)
accToken = db.createAccessToken(user, token, self.lifetime, datetime.now(), lock_bar=False, user_agent=user_agent) db.session.commit()
debug.debug("accesstoken is {{ {} }}".format(accToken))
logger.debug("accesstoken is {{ {} }}".format(accToken))
return token return token
def isSameGroup(self, accToken, groups): def isSameGroup(self, accToken, groups):
@ -122,7 +105,9 @@ class AccesTokenController(metaclass=Singleton):
return db.getAccessTokensFromUser(user) return db.getAccessTokensFromUser(user)
def deleteAccessToken(self, accToken): def deleteAccessToken(self, accToken):
db.deleteAccessToken(accToken) db.session.delete(accToken)
db.session.commit()
#AccessToken.query.filter_by(token=accToken).delete()
def updateAccessToken(self, accToken): def updateAccessToken(self, accToken):
accToken.updateTimestamp() accToken.updateTimestamp()

View File

@ -1,44 +1,37 @@
from .. import Singleton, mailConfig from .. import Singleton, mailConfig
import geruecht.controller.databaseController as dc from ...models.user import User
import geruecht.controller.ldapController as lc
import geruecht.controller.emailController as ec
from geruecht.model.user import User
from datetime import datetime, timedelta from datetime import datetime, timedelta
from geruecht.logger import getDebugLogger from . import mainUserController
from ..mainController import mainJobKindController, mainCreditListController, mainPricelistController, mainUserController, mainWorkerController, mainWorkgroupController, mainJobInviteController, mainJobRequestController, mainRegistrationController, mainPasswordReset from ...database import db
from flask import current_app
from werkzeug.local import LocalProxy
logger = LocalProxy(lambda: current_app.logger)
db = dc.DatabaseController() class MainController(#mainJobKindController.Base,
ldap = lc.LDAPController() #mainCreditListController.Base,
emailController = ec.EmailController() #mainPricelistController.Base,
debug = getDebugLogger()
class MainController(mainJobKindController.Base,
mainCreditListController.Base,
mainPricelistController.Base,
mainUserController.Base, mainUserController.Base,
mainWorkerController.Base, #mainWorkerController.Base,
mainWorkgroupController.Base, #mainWorkgroupController.Base,
mainJobInviteController.Base, #mainJobInviteController.Base,
mainJobRequestController.Base, #mainJobRequestController.Base,
mainRegistrationController.Base, #mainRegistrationController.Base,
mainPasswordReset.Base, #mainPasswordReset.Base,
metaclass=Singleton): metaclass=Singleton):
def __init__(self): def __init__(self):
debug.debug("init UserController") logger.debug("init UserController")
pass pass
def setLockedDay(self, date, locked, hard=False): def setLockedDay(self, date, locked, hard=False):
debug.info( logger.info(
"set day locked on {{ {} }} with state {{ {} }}".format(date, locked)) "set day locked on {{ {} }} with state {{ {} }}".format(date, locked))
retVal = db.setLockedDay(date.date(), locked, hard) retVal = db.setLockedDay(date.date(), locked, hard)
debug.debug("seted day locked is {{ {} }}".format(retVal)) logger.debug("seted day locked is {{ {} }}".format(retVal))
return retVal return retVal
def getLockedDays(self, from_date, to_date): def getLockedDays(self, from_date, to_date):
debug.info("get locked days from {{ {} }} to {{ {} }}".format( logger.info("get locked days from {{ {} }} to {{ {} }}".format(
from_date.date(), to_date.date())) from_date.date(), to_date.date()))
oneDay = timedelta(1) oneDay = timedelta(1)
delta = to_date.date() - from_date.date() delta = to_date.date() - from_date.date()
@ -48,11 +41,11 @@ class MainController(mainJobKindController.Base,
startdate += oneDay startdate += oneDay
lockday = self.getLockedDay(startdate) lockday = self.getLockedDay(startdate)
retVal.append(lockday) retVal.append(lockday)
debug.debug("lock days are {{ {} }}".format(retVal)) logger.debug("lock days are {{ {} }}".format(retVal))
return retVal return retVal
def getLockedDaysFromList(self, date_list): def getLockedDaysFromList(self, date_list):
debug.info("get locked days from list {{ {} }}".format(date_list)) logger.info("get locked days from list {{ {} }}".format(date_list))
retVal = [] retVal = []
for on_date in date_list: for on_date in date_list:
day = datetime(on_date['on_date']['year'], on_date['on_date']['month'], on_date['on_date']['day'], 12) day = datetime(on_date['on_date']['year'], on_date['on_date']['month'], on_date['on_date']['day'], 12)
@ -60,22 +53,22 @@ class MainController(mainJobKindController.Base,
return retVal return retVal
def getLockedDay(self, date): def getLockedDay(self, date):
debug.info("get locked day on {{ {} }}".format(date)) logger.info("get locked day on {{ {} }}".format(date))
now = datetime.now() now = datetime.now()
debug.debug("now is {{ {} }}".format(now)) logger.debug("now is {{ {} }}".format(now))
oldMonth = False oldMonth = False
debug.debug("check if date old month or current month") logger.debug("check if date old month or current month")
for i in range(1, 8): for i in range(1, 8):
if datetime(now.year, now.month, i).weekday() == 2: if datetime(now.year, now.month, i).weekday() == 2:
if now.day < i: if now.day < i:
oldMonth = True oldMonth = True
break break
debug.debug("oldMonth is {{ {} }}".format(oldMonth)) logger.debug("oldMonth is {{ {} }}".format(oldMonth))
lockedYear = now.year lockedYear = now.year
lockedMonth = now.month if now.month < now.month else now.month - \ lockedMonth = now.month if now.month < now.month else now.month - \
1 if oldMonth else now.month 1 if oldMonth else now.month
endDay = 1 endDay = 1
debug.debug("calculate end day of month") logger.debug("calculate end day of month")
lockedYear = lockedYear if lockedMonth != 12 else (lockedYear + 1) lockedYear = lockedYear if lockedMonth != 12 else (lockedYear + 1)
lockedMonth = (lockedMonth + 1) if lockedMonth != 12 else 1 lockedMonth = (lockedMonth + 1) if lockedMonth != 12 else 1
for i in range(1, 8): for i in range(1, 8):
@ -86,33 +79,33 @@ class MainController(mainJobKindController.Base,
monthLockedEndDate = datetime( monthLockedEndDate = datetime(
lockedYear, lockedMonth, endDay) - timedelta(1) lockedYear, lockedMonth, endDay) - timedelta(1)
debug.debug("get lock day from database") logger.debug("get lock day from database")
retVal = db.getLockedDay(date.date()) retVal = db.getLockedDay(date.date())
if not retVal: if not retVal:
debug.debug( logger.debug(
"lock day not exists, retVal is {{ {} }}".format(retVal)) "lock day not exists, retVal is {{ {} }}".format(retVal))
if date.date() <= monthLockedEndDate.date(): if date.date() <= monthLockedEndDate.date():
debug.debug("lock day {{ {} }}".format(date.date())) logger.debug("lock day {{ {} }}".format(date.date()))
self.setLockedDay(date, True) self.setLockedDay(date, True)
retVal = db.getLockedDay(date.date()) retVal = db.getLockedDay(date.date())
else: else:
retVal = {"daydate": date.date(), "locked": False} retVal = {"daydate": date.date(), "locked": False}
debug.debug("locked day is {{ {} }}".format(retVal)) logger.debug("locked day is {{ {} }}".format(retVal))
return retVal return retVal
def __updateDataFromLDAP(self, user): def __updateDataFromLDAP(self, user):
debug.info("update data from ldap for user {{ {} }}".format(user)) logger.info("update data from ldap for user {{ {} }}".format(user))
groups = ldap.getGroup(user.uid) groups = ldap.getGroup(user.uid)
debug.debug("ldap gorups are {{ {} }}".format(groups)) logger.debug("ldap gorups are {{ {} }}".format(groups))
user_data = ldap.getUserData(user.uid) user_data = ldap.getUserData(user.uid)
debug.debug("ldap data is {{ {} }}".format(user_data)) logger.debug("ldap data is {{ {} }}".format(user_data))
user_data['gruppe'] = groups user_data['gruppe'] = groups
user_data['group'] = groups user_data['group'] = groups
user.updateData(user_data) user.updateData(user_data)
db.updateUser(user) db.updateUser(user)
def checkBarUser(self, user): def checkBarUser(self, user):
debug.info("check if user {{ {} }} is baruser") logger.info("check if user {{ {} }} is baruser")
date = datetime.now() date = datetime.now()
zero = date.replace(hour=0, minute=0, second=0, microsecond=0) zero = date.replace(hour=0, minute=0, second=0, microsecond=0)
end = zero + timedelta(hours=12) end = zero + timedelta(hours=12)
@ -121,22 +114,22 @@ class MainController(mainJobKindController.Base,
if date > zero and end > date: if date > zero and end > date:
startdatetime = startdatetime - timedelta(days=1) startdatetime = startdatetime - timedelta(days=1)
enddatetime = startdatetime + timedelta(days=1) enddatetime = startdatetime + timedelta(days=1)
debug.debug("startdatetime is {{ {} }} and enddatetime is {{ {} }}".format( logger.debug("startdatetime is {{ {} }} and enddatetime is {{ {} }}".format(
startdatetime, end)) startdatetime, end))
result = False result = False
if date >= startdatetime and date < enddatetime: if date >= startdatetime and date < enddatetime:
result = db.getWorker(user, startdatetime) result = db.getWorker(user, startdatetime)
debug.debug("worker is {{ {} }}".format(result)) logger.debug("worker is {{ {} }}".format(result))
return True if result else False return True if result else False
def sendMail(self, username): def sendMail(self, username):
debug.info("send mail to user {{ {} }}".format(username)) logger.info("send mail to user {{ {} }}".format(username))
if type(username) == User: if type(username) == User:
user = username user = username
if type(username) == str: if type(username) == str:
user = db.getUser(username) user = db.getUser(username)
retVal = emailController.sendMail(user) retVal = emailController.sendMail(user)
debug.debug("send mail is {{ {} }}".format(retVal)) logger.debug("send mail is {{ {} }}".format(retVal))
return retVal return retVal
def sendAllMail(self): def sendAllMail(self):

View File

@ -1,14 +1,10 @@
from ldap3.core.exceptions import LDAPPasswordIsMandatoryError, LDAPBindError from flaschengeist.system.exceptions import UsernameExistLDAP, LDAPExcetpion, PermissionDenied
from flaschengeist.system.models.user import User
from flaschengeist.system.database import db
from geruecht.exceptions import UsernameExistLDAP, LDAPExcetpion, PermissionDenied from flask import Blueprint, current_app
import geruecht.controller.databaseController as dc from werkzeug.local import LocalProxy
import geruecht.controller.ldapController as lc logger = LocalProxy(lambda: current_app.logger)
from geruecht.logger import getDebugLogger
from geruecht.model.user import User
db = dc.DatabaseController()
ldap = lc.LDAPController()
debug = getDebugLogger()
class Base: class Base:
def getAllStatus(self): def getAllStatus(self):
@ -167,13 +163,14 @@ class Base:
ldap.login(username, password) ldap.login(username, password)
def loginUser(self, username, password): def loginUser(self, username, password):
debug.info("login user {{ {} }}".format(username)) logger.info("login user {{ {} }}".format(username))
try: user = User.query.filter_by(uid=username).first()
user = self.getUser(username) if user is None:
debug.debug("user is {{ {} }}".format(user)) user = User(uid=username)
user.password = password for backend in current_app.config['FG_AUTH_BACKENDS']:
ldap.login(username, password) b = backend()
return user if b.login(user, password):
except PermissionDenied as err: db.session.add(user)
debug.debug("permission is denied", exc_info=True) db.session.commit()
raise err return user
raise PermissionDenied()

View File

@ -1,35 +1,29 @@
from functools import wraps from functools import wraps
from flask import current_app from flask import current_app, request, jsonify
from werkzeug.local import LocalProxy from flaschengeist import logger
logger = LocalProxy(lambda: current_app.logger)
def login_required(**kwargs): def login_required(**kwargs):
import geruecht.controller.accesTokenController as ac from .controller.accesTokenController import AccesTokenController
from geruecht.model import BAR, USER, MONEY, GASTRO, VORSTAND, EXTERN accessController = AccesTokenController()
from flask import request, jsonify #if "groups" in kwargs:
accessController = ac.AccesTokenController() # groups = kwargs["groups"]
groups = [USER, BAR, GASTRO, MONEY, VORSTAND, EXTERN] #if "bar" in kwargs:
bar = False # bar = kwargs["bar"]
if "groups" in kwargs: #logger.debug("groups are {{ {} }}".format(groups))
groups = kwargs["groups"]
if "bar" in kwargs:
bar = kwargs["bar"]
logger.debug("groups are {{ {} }}".format(groups))
def real_decorator(func): def real_decorator(func):
@wraps(func) @wraps(func)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
token = request.headers.get('Token') token = request.headers.get('Token')
logger.debug("token is {{ {} }}".format(token)) logger.debug("token is {{ {} }}".format(token))
accToken = accessController.validateAccessToken(token, groups) accToken = accessController.validateAccessToken(token, None)
logger.debug("accToken is {{ {} }}".format(accToken)) logger.debug("accToken is {{ {} }}".format(accToken))
kwargs['accToken'] = accToken kwargs['accToken'] = accToken
if accToken: if accToken:
logger.debug("token {{ {} }} is valid".format(token)) logger.debug("token {{ {} }} is valid".format(token))
if accToken.lock_bar and not bar: # if accToken.lock_bar and not bar:
return jsonify({"error": "error", # return jsonify({"error": "error",
"message": "permission forbidden"}), 403 # "message": "permission forbidden"}), 403
return func(*args, **kwargs) return func(*args, **kwargs)
else: else:
logger.warning("token {{ {} }} is not valid".format(token)) logger.warning("token {{ {} }} is not valid".format(token))

View File

@ -52,7 +52,7 @@ class AccessToken(db.Model):
return dic return dic
def __eq__(self, token): def __eq__(self, token):
return True if self.token == token else False return self.token == token
def __sub__(self, other): def __sub__(self, other):
return other - self.timestamp return other - self.timestamp

View File

@ -48,6 +48,16 @@ class User(db.Model):
if 'displayname' in data: if 'displayname' in data:
self.displayname = data['displayname'] self.displayname = data['displayname']
def toJSON(self):
return {
"uid": self.uid,
"displayname": self.displayname,
"firstname": self.firstname,
"lastname": self.lastname,
"mail": self.mail,
"groups": self.groups
}
class UserAttribute(db.Model): class UserAttribute(db.Model):
__tablename__ = 'userAttribute' __tablename__ = 'userAttribute'

View File

@ -13,7 +13,7 @@ setup(
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_ldapconn", "flask_cors"],
entry_points = { entry_points = {
'flaschengeist.plugin': [ 'flaschengeist.plugin': [
'user = flaschengeist.modules.user:register' 'auth = flaschengeist.modules.auth:register'
], ],
'flaschengeist.auth': [ 'flaschengeist.auth': [
'plain_auth = flaschengeist.modules.auth_plain:AuthPlain' 'plain_auth = flaschengeist.modules.auth_plain:AuthPlain'