Fixed AccessTokenController. Fixed typos and styling.

This commit is contained in:
Ferdinand Thiessen 2020-09-02 01:09:24 +02:00
parent 66dcfa80b1
commit 3256787d64
11 changed files with 109 additions and 102 deletions

View File

@ -9,8 +9,6 @@ _modpath = Path(__file__).parent
from flask import Flask
from flask_cors import CORS
from flask_ldapconn import LDAPConn
import ssl
import pkg_resources, yaml
from logging.config import dictConfig

View File

@ -10,13 +10,16 @@ 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
import flaschengeist.system.controller.accessTokenController as ac
from flaschengeist.system.models.accessToken import AccessToken
logger = LocalProxy(lambda: current_app.logger)
accesTokenController = LocalProxy(lambda: ac.AccesTokenController())
access_controller = LocalProxy(lambda: ac.AccessTokenController())
auth_bp = Blueprint('auth', __name__)
def register():
return auth_bp
@ -24,31 +27,14 @@ def register():
## 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)
accesTokenController.clearExpired()
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
A JSON-File with user information and created token or errors
"""
logger.debug("Start log in.")
data = request.get_json()
@ -58,12 +44,12 @@ def _login():
logger.debug("username is {{ {} }}".format(username))
try:
logger.debug("search {{ {} }} in database".format(username))
mainController = mc.MainController()
user = mainController.loginUser(username, password)
main_controller = mc.MainController()
user = main_controller.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")
token = access_controller.create(user, user_agent=request.user_agent)
logger.debug("access token is {{ {} }}".format(token))
logger.debug("validate access token")
dic = user.toJSON()
dic["accessToken"] = token
logger.info("User {{ {} }} success login.".format(username))
@ -76,16 +62,34 @@ def _login():
logger.error("exception in login.", exc_info=True)
return jsonify({"error": "permission denied"}), 401
@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")
access_controller.deleteAccessToken(accToken)
access_controller.clearExpired()
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("/user/getAccessTokens", methods=['GET', 'POST'])
#@auth_bp.route("/accessTokens", methods=['GET', 'POST'])
@login_required()
def _getAccessTokens(**kwargs):
try:
if request.method == 'POST':
data = request.get_json()
accesTokenController.deleteAccessToken(accToken)
delAccToken = AccessToken(data['id'], kwargs['accToken'].user, None, None, None)
accesTokenController.deleteAccessToken(delAccToken)
tokens = accesTokenController.getAccessTokensFromUser(kwargs['accToken'].user)
token = AccessToken(data['id'], kwargs['accToken'].user, None, None, None)
access_controller.delete_token(token)
tokens = access_controller.getAccessTokensFromUser(kwargs['accToken'].user)
r = [t.toJSON() for t in tokens]
logger.debug("return {{ {} }}".format(r))
return jsonify(r)
@ -93,6 +97,7 @@ def _getAccessTokens(**kwargs):
logger.debug("exception", exc_info=True)
return jsonify({"error": str(err)}), 500
@auth_bp.route("/getLifetime", methods=['GET'])
@login_required()
def _getLifeTime(**kwargs):
@ -105,22 +110,23 @@ def _getLifeTime(**kwargs):
logger.warning("exception in get lifetime of accesstoken.", exc_info=True)
return jsonify({"error": str(err)}), 500
@auth_bp.route("/setLifetime", methods=['POST'])
@login_required()
def _saveLifeTime(**kwargs):
try:
accToken = kwargs['accToken']
logger.debug("save lifetime for accessToken {{ {} }}".format(accToken))
token = kwargs['accToken']
logger.debug("save lifetime for access token {{ {} }}".format(token))
data = request.get_json()
lifetime = data['value']
logger.debug("lifetime is {{ {} }}".format(lifetime))
logger.info("set lifetime {{ {} }} to accesstoken {{ {} }}".format(
lifetime, accToken))
accToken.lifetime = lifetime
logger.info("update accesstoken timestamp")
accToken = accesTokenController.updateAccessToken(accToken)
return jsonify({"value": accToken.lifetime })
logger.info("set lifetime {{ {} }} to access token {{ {} }}".format(
lifetime, token))
token.lifetime = lifetime
logger.info("update access token timestamp")
token = access_controller.update(token)
return jsonify({"value": token.lifetime })
except Exception as err:
logger.warning(
"exception in save lifetime for accesstoken.", exc_info=True)
"exception in save lifetime for access token.", exc_info=True)
return jsonify({"error": str(err)}), 500

View File

@ -2,9 +2,10 @@ 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
from ldap3 import SUBTREE
import ssl
class AuthLDAP(modules.Auth):
_default = {
'PORT': '389',
@ -17,14 +18,14 @@ class AuthLDAP(modules.Auth):
config[name] = self._default[name]
app.config.update(
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
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'],
@ -75,7 +76,9 @@ class AuthLDAP(modules.Auth):
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))
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")
@ -83,7 +86,7 @@ class AuthLDAP(modules.Auth):
if conn.entries:
debug.warning("username already exists", exc_info=True)
raise UsernameExistLDAP("Username already exists in LDAP")
#create modifyer
# create modifyer
mody = {}
if 'username' in attributes:
mody['uid'] = [(MODIFY_REPLACE, [attributes['username']])]
@ -103,7 +106,8 @@ class AuthLDAP(modules.Auth):
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'])
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'])
@ -120,15 +124,18 @@ class AuthLDAP(modules.Auth):
try:
groups = []
self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid={})'.format(uid), SUBTREE, attributes=['gidNumber'])
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'])
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'])
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])

View File

@ -1,6 +1,10 @@
import hashlib, binascii, os
import binascii
import hashlib
import os
import flaschengeist.modules as modules
class AuthPlain(modules.Auth):
def login(self, user, password):
if not user:
@ -18,7 +22,6 @@ class AuthPlain(modules.Auth):
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 = 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

@ -14,10 +14,10 @@ default = {
config = configparser.ConfigParser()
config.read_dict(default)
pathes = [_modpath, Path.home()/".config"]
paths = [_modpath, Path.home()/".config"]
if 'FLASCHENGEIST_CONF' in os.environ:
pathes.append(Path(os.environ.get("FLASCHENGEIST_CONF")))
for loc in pathes:
paths.append(Path(os.environ.get("FLASCHENGEIST_CONF")))
for loc in paths:
try:
with (loc/"flaschengeist.cfg").open() as source:
config.read_file(source)

View File

@ -1,7 +1,7 @@
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]

View File

@ -15,7 +15,6 @@ class AccessTokenController(metaclass=Singleton):
This Class create, delete, find and manage AccesToken.
Attributes:
tokenList: List of currents AccessToken
lifetime: Variable for the Lifetime of one AccessToken in seconds.
"""
instance = None
@ -29,34 +28,34 @@ class AccessTokenController(metaclass=Singleton):
logger.debug("init accesstoken controller")
self.lifetime = lifetime
def validateAccessToken(self, token, roles):
""" Verify Accestoken
def validate(self, token, roles):
""" Verify access token
Verify an Accestoken and Group so if the User has permission or not.
Retrieves the accestoken if valid else retrieves False
Verify an AccessToken and Group so if the User has permission or not.
Retrieves the access token if valid else retrieves False
Args:
token: Token to verify.
roles: Roles needed to access restricted routes
Returns:
An the AccesToken for this given Token or False.
An the AccessToken for this given Token or False.
"""
logger.debug("check token {{ {} }} is valid".format(token))
for accToken in AccessToken.query.filter_by(token=token):
endTime = accToken.timestamp + timedelta(seconds=accToken.lifetime)
time_end = accToken.timestamp + timedelta(seconds=accToken.lifetime)
now = datetime.utcnow()
logger.debug("now is {{ {} }}, endtime is {{ {} }}".format(now, endTime))
if now <= endTime:
logger.debug("now is {{ {} }}, endtime is {{ {} }}".format(now, time_end))
if now <= time_end:
logger.debug("check if token {{ {} }} is same as {{ {} }}".format(token, accToken))
if not roles or (roles and self.userHasRole(accToken.user, roles)):
accToken.updateTimestamp()
db.session.commit()
return accToken
else:
logger.debug("accesstoken is {{ {} }} out of date".format(accToken))
logger.debug("access token is {{ {} }} out of date".format(accToken))
db.session.delete(accToken)
db.session.commit()
logger.debug("no valid accesstoken with token: {{ {} }} and group: {{ {} }}".format(token, group))
logger.debug("no valid access token with token: {{ {} }} and group: {{ {} }}".format(token, roles))
return False
def userHasRole(self, user, roles):
@ -66,24 +65,25 @@ class AccessTokenController(metaclass=Singleton):
return True
return False
def createAccesToken(self, user, user_agent=None):
def create(self, user, user_agent=None):
""" Create an AccessToken
Create an AccessToken for an User and add it to the tokenList.
Args:
user: For wich User is to create an AccessToken
user: For which User is to create an AccessToken
user_agent: User agent to identify session
Returns:
A created Token for User
"""
logger.debug("creat accesstoken")
token = secrets.token_hex(16)
accToken = AccessToken(token=token, user=user, lifetime=self.lifetime, browser=user_agent.browser, platform=user_agent.platform)
db.session.add(accToken)
logger.debug("create access token")
token_str = secrets.token_hex(16)
token = AccessToken(token=token_str, user=user, lifetime=self.lifetime, browser=user_agent.browser, platform=user_agent.platform)
db.session.add(token)
db.session.commit()
logger.debug("accesstoken is {{ {} }}".format(accToken))
logger.debug("access token is {{ {} }}".format(token))
return token
def getAccessTokensFromUser(self, user):
@ -96,12 +96,12 @@ class AccessTokenController(metaclass=Singleton):
AccessToken.query.filter_by(token=accessToken).delete()
db.session.commit()
def updateAccessToken(self, accessToken):
accessToken.updateTimestamp()
@staticmethod
def update_token(self, token):
token.updateTimestamp()
db.session.commit()
return accessToken
def clearExpired(self):
def clear_expired(self):
logger.debug("Clear expired AccessToken")
mightExpired = datetime.utcnow() - timedelta(seconds=self.lifetime)
tokens = AccessToken.query.filter(AccessToken.timestamp < mightExpired)

View File

@ -1,10 +1,11 @@
from functools import wraps
from flask import current_app, request, jsonify
from flask import request, jsonify
from flaschengeist import logger
def login_required(**kwargs):
from .controller.accessTokenController import AccessTokenController
accessController = AccessTokenController()
ac_controller = AccessTokenController()
roles = None
if "roles" in kwargs:
roles = kwargs["roles"]
@ -14,10 +15,10 @@ def login_required(**kwargs):
def wrapper(*args, **kwargs):
token = request.headers.get('Token')
logger.debug("token is {{ {} }}".format(token))
accToken = accessController.validateAccessToken(token, roles)
logger.debug("accToken is {{ {} }}".format(accToken))
kwargs['accToken'] = accToken
if accToken:
access_token = ac_controller.validate(token, roles)
logger.debug("accToken is {{ {} }}".format(access_token))
kwargs['accToken'] = access_token
if access_token:
logger.debug("token {{ {} }} is valid".format(token))
return func(*args, **kwargs)
else:

View File

@ -1,14 +1,6 @@
class PermissionDenied(Exception):
pass
class UsernameExistDB(Exception):
pass
class UsernameExistLDAP(Exception):
pass
class DatabaseExecption(Exception):
pass
class LDAPExcetpion(Exception):
pass
class DayLocked(Exception):
pass
class TansactJobIsAnswerdException(Exception):
pass

View File

@ -6,6 +6,7 @@ from secrets import compare_digest
logger = LocalProxy(lambda: current_app.logger)
class AccessToken(db.Model):
""" Model for an AccessToken

View File

@ -1,6 +1,4 @@
from datetime import datetime
from ..database import db
from .accessToken import AccessToken
from sqlalchemy.orm.collections import attribute_mapped_collection
from flask import current_app
from werkzeug.local import LocalProxy
@ -11,6 +9,7 @@ association_table = db.Table('user_group',
db.Column('group_id', db.Integer, db.ForeignKey('group.id'))
)
class User(db.Model):
""" Database Object for User