import time import connexion from jose import JWTError, jwt import werkzeug import os import mariadb from collections import namedtuple from pbkdf2 import crypt DB_USER = os.environ["DB_USER"] DB_PASS = os.environ["DB_PASS"] DB_HOST = os.environ["DB_HOST"] DB_NAME = os.environ["DB_NAME"] class NoUserException(Exception): pass class ManyUsersException(Exception): pass class PasswordMismatchException(Exception): pass UserEntry = namedtuple('UserEntry', ['id', 'login', 'issuer', 'secret', 'expiry', 'claims']) def getUserEntryFromDB(application: str, login: str): conn = None cur = None try: conn = mariadb.connect(user = DB_USER, password = DB_PASS, host = DB_HOST, database = DB_NAME) conn.autocommit = False cur = conn.cursor(dictionary=True) cur.execute("SELECT id, password, issuer, secret, expiry FROM user_application_and_issuer " + " WHERE application = ? AND login = ?", [application, login]) resObj = cur.next() print("DEBUG: getUserEntryFromDB: resObj: {}".format(resObj)) if not resObj: raise NoUserException() invObj = cur.next() if invObj: raise ManyUsersException() userId = resObj["id"] cur.execute("SELECT user, `key`, `value` FROM claims_for_user where user = ?", [userId]) claims = {} for claimObj in cur: print("DEBUG: getUserEntryFromDB: add claim {} -> {}".format(claimObj["key"], claimObj["value"])) if claimObj["key"] in claims: if isinstance(claimObj["key"], list): claims[claimObj["key"]].append(claimObj["value"]) else: claims[claimObj["key"]] = [ claims[claimObj["key"]] ] claims[claimObj["key"]].append(claimObj["value"]) else: claims[claimObj["key"]] = claimObj["value"] userEntry = UserEntry(id=userId, login=login, secret=resObj["secret"], issuer=resObj["issuer"], expiry=resObj["expiry"], claims=claims) return userEntry, resObj["password"] except mariadb.Error as err: raise Exception("Error when connecting to database: {}".format(err)) finally: if cur: cur.close() if conn: conn.rollback() conn.close() def getUserEntry(application, login, password): userEntry, pwhash = getUserEntryFromDB(application, login) if pwhash != crypt(password, pwhash): raise PasswordMismatchException() return userEntry def generateToken(**args): try: body = args["body"] application = body["application"] login = body["login"] password = body["password"] userEntry = getUserEntry(application, login, password) timestamp = int(time.time()) payload = { "iss": userEntry.issuer, "iat": int(timestamp), "exp": int(timestamp + userEntry.expiry), "sub": str(userEntry.id) } for claim in userEntry.claims.items(): # print("DEBUG: generateToken: add claim {} -> {}".format(claim[0], claim[1])) payload["x-{}".format(claim[0])] = claim[1] return jwt.encode(payload, userEntry.secret) except NoUserException: print("ERROR: generateToken: no user found, login or application wrong") raise werkzeug.exceptions.Unauthorized() except ManyUsersException: print("ERROR: generateToken: too many users found") raise werkzeug.exceptions.Unauthorized() except PasswordMismatchException: print("ERROR: generateToken: wrong password") raise werkzeug.exceptions.Unauthorized() except KeyError: print("ERROR: generateToken: application, login or password missing") raise werkzeug.exceptions.Unauthorized() except Exception as e: print("ERROR: generateToken: unspecific exception: {}".format(str(e))) raise werkzeug.exceptions.Unauthorized()