Fixed AccessTokenController. Fixed typos and styling.
This commit is contained in:
parent
66dcfa80b1
commit
3256787d64
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
|
@ -6,6 +6,7 @@ from secrets import compare_digest
|
|||
|
||||
logger = LocalProxy(lambda: current_app.logger)
|
||||
|
||||
|
||||
class AccessToken(db.Model):
|
||||
""" Model for an AccessToken
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue