authservice/auth.py

173 lines
5.4 KiB
Python
Raw Permalink Normal View History

2021-01-25 21:52:52 +01:00
import time
import connexion
from jose import JWTError, jwt, jwe
import json
2021-01-26 13:43:09 +01:00
import werkzeug
2021-01-25 21:52:52 +01:00
import os
2021-05-11 15:16:13 +02:00
import psycopg2
2021-01-26 13:43:09 +01:00
from collections import namedtuple
2021-01-26 21:27:17 +01:00
from pbkdf2 import crypt
2021-05-11 16:48:02 +02:00
from loguru import logger
2021-01-25 21:52:52 +01:00
DB_USER = os.environ["DB_USER"]
DB_PASS = os.environ["DB_PASS"]
DB_HOST = os.environ["DB_HOST"]
DB_NAME = os.environ["DB_NAME"]
2021-01-27 10:57:54 +01:00
JWT_ISSUER = os.environ["JWT_ISSUER"]
2021-05-06 16:37:32 +02:00
2021-01-27 10:57:54 +01:00
2021-01-25 21:52:52 +01:00
2021-01-26 21:27:17 +01:00
class NoUserException(Exception):
pass
class ManyUsersException(Exception):
pass
class PasswordMismatchException(Exception):
pass
2021-01-26 13:43:09 +01:00
2021-05-11 16:48:02 +02:00
UserEntry = namedtuple('UserEntry', ['id', 'login', 'pwhash', 'expiry', 'claims'])
2021-05-07 13:28:12 +02:00
2021-05-06 16:37:32 +02:00
JWT_PRIV_KEY = ""
2021-05-07 13:28:12 +02:00
try:
JWT_PRIV_KEY = os.environ["JWT_PRIV_KEY"]
except KeyError:
with open('/opt/app/config/authservice.key', 'r') as f:
JWT_PRIV_KEY = f.read()
2021-05-06 16:37:32 +02:00
JWT_PUB_KEY = ""
2021-05-07 13:28:12 +02:00
try:
JWT_PUB_KEY = os.environ["JWT_PUB_KEY"]
except KeyError:
with open('/opt/app/config/authservice.pub', 'r') as f:
JWT_PUB_KEY = f.read()
2021-01-26 13:43:09 +01:00
2021-01-26 22:06:39 +01:00
def getUserEntryFromDB(application: str, login: str):
2021-01-25 21:52:52 +01:00
conn = None
cur = None
try:
2021-05-11 15:16:13 +02:00
conn = psycopg2.connect(user = DB_USER, password = DB_PASS,
host = DB_HOST, database = DB_NAME)
2021-01-25 21:52:52 +01:00
conn.autocommit = False
2021-05-11 16:48:02 +02:00
userObj = None
with conn.cursor() as cur:
cur.execute("SELECT id, pwhash, expiry FROM user_application_v" +
" WHERE application = %s AND login = %s",
(application, login))
userObj = cur.fetchone()
logger.debug("userObj: {}".format(userObj))
2021-05-11 16:48:02 +02:00
if not userObj:
raise NoUserException()
invObj = cur.fetchone()
if invObj:
raise ManyUsersException()
2021-01-26 13:43:09 +01:00
claims = {}
2021-05-11 16:48:02 +02:00
with conn.cursor() as cur:
cur.execute('SELECT key, value FROM claims_for_user_v where "user" = %s and application = %s',
(userObj[0], application))
for claimObj in cur:
logger.debug("add claim {} -> {}".format(claimObj[0], claimObj[1]))
2021-05-11 16:48:02 +02:00
if claimObj[0] in claims:
if isinstance(claims[claimObj[0]], list):
claims[claimObj[0]].append(claimObj[1])
else:
claims[claimObj[0]] = [ claims[claimObj[0]] ]
claims[claimObj[0]].append(claimObj[1])
2021-01-26 13:43:09 +01:00
else:
2021-05-11 16:48:02 +02:00
claims[claimObj[0]] = claimObj[1]
2021-01-26 13:43:09 +01:00
2021-05-11 16:48:02 +02:00
userEntry = UserEntry(id=userObj[0], login=login, pwhash=userObj[1], expiry=userObj[2], claims=claims)
2021-01-25 21:52:52 +01:00
2021-05-11 16:48:02 +02:00
return userEntry
except psycopg2.Error as err:
2021-01-25 21:52:52 +01:00
raise Exception("Error when connecting to database: {}".format(err))
finally:
if conn:
conn.close()
2021-01-26 22:06:39 +01:00
def getUserEntry(application, login, password):
2021-05-11 16:48:02 +02:00
userEntry = getUserEntryFromDB(application, login)
if userEntry.pwhash != crypt(password, userEntry.pwhash):
2021-01-26 21:27:17 +01:00
raise PasswordMismatchException()
2021-01-26 22:06:39 +01:00
return userEntry
2021-01-25 21:52:52 +01:00
2021-01-26 13:43:09 +01:00
def generateToken(**args):
2021-01-25 21:52:52 +01:00
try:
2021-01-26 13:43:09 +01:00
body = args["body"]
application = ""
login = ""
password = ""
if (("application" in body) and
("login" in body) and
("password" in body)):
application = body["application"]
login = body["login"]
password = body["password"]
elif ("encAleTuple" in body):
clearContent = jwe.decrypt(body["encAleTuple"], JWT_PRIV_KEY)
clearObj = json.loads(clearContent)
application = clearObj["application"]
login = clearObj["login"]
password = clearObj["password"]
else:
raise KeyError("Neither application, login and password nor encAleTuple given")
logger.debug(f"Tuple: {application} {login} {password}")
2021-01-26 21:27:17 +01:00
2021-01-26 22:06:39 +01:00
userEntry = getUserEntry(application, login, password)
2021-01-26 21:27:17 +01:00
2021-01-26 13:43:09 +01:00
timestamp = int(time.time())
payload = {
2021-01-27 10:57:54 +01:00
"iss": JWT_ISSUER,
2021-01-26 13:43:09 +01:00
"iat": int(timestamp),
"exp": int(timestamp + userEntry.expiry),
2021-05-07 14:40:40 +02:00
"sub": str(userEntry.id),
"aud": application
2021-01-26 13:43:09 +01:00
}
2021-05-11 16:48:02 +02:00
logger.debug("claims: {}".format(userEntry.claims))
2021-01-26 13:43:09 +01:00
for claim in userEntry.claims.items():
logger.debug("add claim {}".format(claim))
2021-01-27 13:31:34 +01:00
payload[claim[0]] = claim[1]
2021-01-26 13:43:09 +01:00
2021-05-06 16:37:32 +02:00
return jwt.encode(payload, JWT_PRIV_KEY, algorithm='RS256')
2021-01-26 13:43:09 +01:00
except NoUserException:
logger.error("no user found, login or application wrong")
2021-01-26 13:43:09 +01:00
raise werkzeug.exceptions.Unauthorized()
except ManyUsersException:
logger.error("too many users found")
2021-01-26 13:43:09 +01:00
raise werkzeug.exceptions.Unauthorized()
2021-01-26 21:27:17 +01:00
except PasswordMismatchException:
logger.error("wrong password")
2021-01-26 21:27:17 +01:00
raise werkzeug.exceptions.Unauthorized()
2021-01-26 13:43:09 +01:00
except KeyError:
logger.error("application, login or password missing")
2021-01-26 13:43:09 +01:00
raise werkzeug.exceptions.Unauthorized()
except Exception as e:
logger.error("unspecific exception: {}".format(str(e)))
2021-01-26 13:43:09 +01:00
raise werkzeug.exceptions.Unauthorized()
2021-05-06 16:37:32 +02:00
def getPubKey():
return JWT_PUB_KEY
2021-05-11 16:48:02 +02:00
def decodeToken(token):
try:
return jwt.decode(token, JWT_PUB_KEY, audience="test")
except JWTError as e:
logger.error("{}".format(e))
2021-05-11 16:48:02 +02:00
raise werkzeug.exceptions.Unauthorized()
def testToken(user, token_info):
2021-06-17 18:50:05 +02:00
return {
"message": f"You are user_id {user} and the provided token has been signed by this issuers. Fine.",
"details": token_info
}
2021-05-11 16:48:02 +02:00